home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / misc / volume7 / gosip2 < prev    next >
Encoding:
Text File  |  1989-07-08  |  99.9 KB  |  3,001 lines

  1. Newsgroups: comp.sources.misc
  2. From: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
  3. Subject: v07i065: Gosip -- fixes a major bug
  4. Reply-To: Andreas Pagel <mauhk@cu.warwick.ac.uk>
  5. Organization: Mathematics Dept., Warwick University, UK
  6.  
  7. Posting-number: Volume 7, Issue 65
  8. Submitted-by: mauhk@cu.warwick.ac.uk (Andreas Pagel)
  9. Archive-name: gosip2
  10.  
  11. [Too late....  ++bsa]
  12.  
  13. I noticed a terrible error in the gosip shar I sent you recently, in that I
  14. failed to specify customisation correctly.  I also noticed you haven't sent it
  15. out yet, so could you post this (hopefully) correct version instead?
  16.  
  17. thanks,
  18. Andreas.
  19. ---------------------------------cut here---------------------------------
  20. #! /bin/sh
  21. # This is a shell archive.  Remove anything before this line, then unpack
  22. # it by saving it into a file and typing "sh file".  To overwrite existing
  23. # files, type "sh file -c".  You can also feed this as standard input via
  24. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  25. # will see the following message at the end:
  26. #        "End of shell archive."
  27. # Contents:  History Makefile README allgoss.1 cat.c cat.h control.c
  28. #   control.h dw.1 edit.c edit.h global.h gosip.1 history.c history.h
  29. #   lu.1 lu.c lu.h main.c main.h notes setup util.c util.h
  30. # Wrapped by mauhk@orchid on Wed Jun 28 16:23:05 1989
  31. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  32. if test -f 'History' -a "${1}" != "-c" ; then 
  33.   echo shar: Will not clobber existing file \"'History'\"
  34. else
  35. echo shar: Extracting \"'History'\" \(2950 characters\)
  36. sed "s/^X//" >'History' <<'END_OF_FILE'
  37. XAPS Gosip: History
  38. X==================
  39. X
  40. XThe original idea was conceived by Jonathan Hughes of Warwick University, back
  41. Xin 1987.  He simply created two writable files, gossip and help, in his home
  42. Xdirectory and invited people to edit them: gossip for general chatting, and
  43. Xhelp for queries about the UNIX system or programming assignments.
  44. X
  45. XI soon realised that if two people edited a file simultaneously, only one
  46. Xperson's changes would be saved.  So I wrote my first ever shell program,
  47. Xsomething like:
  48. X
  49. Xif test -f tmp.gossip
  50. Xthen
  51. X  echo "Sorry, gossip is being edited."
  52. X  cat text.gossip
  53. Xelse
  54. X  touch tmp.gossip
  55. X  ded text.gossip     # ded is the local editor
  56. X  rm temp.gossip
  57. Xfi
  58. X
  59. Xand a similar one for the help file.  Took me weeks to get that right!  This
  60. Xevolved for a bit, but didn't really get drastically better.  As more gosip
  61. Xfiles were created, Geoff Rimmer had the idea of keeping just one version of
  62. Xthe source, hardlinked to all the different gosip files, with all occurrences
  63. Xof "gossip" replaced by "$0".  He also thought of some options to give it, eg.
  64. Xto send the file to a printer, or to list it rather than edit it.
  65. X
  66. XMike Taylor deserves a mention as being the one to polish up this basic idea
  67. Xover the next year or so.  He added in bits to keep a record of who edited
  68. Xwhich file, and he wrote a separate shell program (lu) that would list this
  69. Xinformation, and would also print a short summary of the status of each file
  70. X(the basic help and gossip files soon expanded to include events, suggest,
  71. Xjokes, etc.).
  72. X
  73. XSome of these early versions had some pretty bad bugs in.  Of particular note
  74. Xwas the time that the shell script was made setuid, in order to prevent a
  75. Xcertain person from deleting text.gossip.  Of course, one could then spawn a
  76. Xsetuid shell from the editor - I had about a week of access to Mike's code
  77. Xbefore he finally noticed this security hole.
  78. X
  79. XA major problem all the time was that the lock files/directories (both were
  80. Xused at various stages) used to ensure exclusive access would frequently not
  81. Xbe deleted after a given file was no longer being edited.  The final version
  82. Xof the shell script for gosip included code that would delete lock files more
  83. Xthan half an hour old, on the assumption that their continued existence was a
  84. Xmanifestation of this problem.  There were also a few other bugs in it, and it
  85. Xwas quite slow too.
  86. X
  87. XAt this point, I began to re-write the whole thing in C.  I was still fairly
  88. Xnew to C at that stage of my life, and tried simply to convert the shell
  89. Xversion into C, almost on a line for line basis.  The program went through a
  90. Xlot of refinements, most of which are recorded in the file 'notes' - at
  91. Xaround version 31 I had the brilliant idea of keeping a list of my changes in
  92. Xthis file.
  93. X
  94. XFor the user, the C version offered greater speed, and that was about all.
  95. XHowever, the mutual exclusion problem was finally solved in version 73, and
  96. Xthere was much rejoicing.
  97. X
  98. XAndreas Pagel.
  99. END_OF_FILE
  100. if test 2950 -ne `wc -c <'History'`; then
  101.     echo shar: \"'History'\" unpacked with wrong size!
  102. fi
  103. # end of 'History'
  104. fi
  105. if test -f 'Makefile' -a "${1}" != "-c" ; then 
  106.   echo shar: Will not clobber existing file \"'Makefile'\"
  107. else
  108. echo shar: Extracting \"'Makefile'\" \(211 characters\)
  109. sed "s/^X//" >'Makefile' <<'END_OF_FILE'
  110. X# Makefile for gosip (rather short because I lost the previous one).
  111. X
  112. XCFLAGS=
  113. Xsources=cat.c control.c edit.c history.c lu.c main.c util.c
  114. X
  115. Xreal:
  116. X    $(CC) $(CFLAGS) -DREAL -O -o real $(sources)
  117. X    rm *.o
  118. X    strip real
  119. END_OF_FILE
  120. if test 211 -ne `wc -c <'Makefile'`; then
  121.     echo shar: \"'Makefile'\" unpacked with wrong size!
  122. fi
  123. # end of 'Makefile'
  124. fi
  125. if test -f 'README' -a "${1}" != "-c" ; then 
  126.   echo shar: Will not clobber existing file \"'README'\"
  127. else
  128. echo shar: Extracting \"'README'\" \(4961 characters\)
  129. sed "s/^X//" >'README' <<'END_OF_FILE'
  130. XAPS Gosip
  131. X=========
  132. X
  133. XGosip is a program that allows a user community to interchange lively debate,
  134. Xfriendly banter, helpful insights, or anything else, by means of editing a
  135. Xglobally writable (set of) file(s).  Gosip itself is also a term used to stand
  136. Xfor any particular file's name, a bit like the shell meta character ``*''. [1]
  137. X
  138. XThe file 'History' gives some details on how the whole thing evolved, but the
  139. Xbasic idea is that the compiled program is (hard-)linked to whatever files you
  140. Xwish to support, eg. help.  Then when anyone runs 'help', the program invokes
  141. Xan editor on the file text.help while ensuring exclusive access to that file.
  142. X
  143. XObviously the binaries need to be kept in a directory which is in every
  144. Xpotential user's path.  Here at Warwick, we have a system called 'Newwords+'
  145. Xwhich provides not only gosip, but quite a few other extra commands written by
  146. Xthe users for the user community.  It basically works by putting a
  147. Xsubdirectory of the newwords+ administrator in every subscriber's path.
  148. X
  149. XContents
  150. X========
  151. X
  152. XIn the shar file you should find the source files edit.c main.c util.c lu.c
  153. Xhistory.c cat.c control.c with corresponding header files plus global.h.  Dw
  154. Xis a shell script that displays changes in gosip files [3], and finally setup
  155. Xis a shell program that installs gosip files (more below).
  156. X
  157. XIn addition, the following manual pages are supplied: allgoss.1, lu.1, dw.1,
  158. Xgosip.1.  Other documentation provided: README you already know about, History
  159. Xas mentioned above and notes, which is mainly a record of changes by version
  160. Xnumber.
  161. X
  162. XCustomisation
  163. X=============
  164. X
  165. XYou will need to alter some file and directory names and other #define's:
  166. X
  167. X(1)  In global.h, change DATA_FILE_DIRECTORY to a directory which you [will] 
  168. X     own and which can be accessed by all potential gosip users.
  169. X
  170. X(2)  Change SECRET_DIR to something not easily guessed.
  171. X
  172. X(3)  In main.c, function init_file_names(), change the names of history and
  173. X     last files (and lock files if used).  The history and last files should
  174. X     not be edited directly by anyone: giving them unusual names in an
  175. X     unreadable directory makes this less likely to occur.
  176. X
  177. X(4)  In setup, change the definitions of the above accordingly. 
  178. X
  179. X(5)  In control.h, you might like to specify yourself as the SUPER_USER.
  180. X     SUPER_USER is the only one who can use the -# and -@ options, which are
  181. X     used to deny or restore access to gosip (normally used when a new version
  182. X     is being installed).  You might like to change DOWN_FILE as well.
  183. X
  184. X(6)  In global.h, alter MAX_FILE_LENGTH to reflect your choice of file names.
  185. X
  186. XFor quite a while lockf() did not work on our system.  I have left in the
  187. Xprevious locking code, based on lock files and flock(), which you can use if
  188. Xyour lockf() is broken: add 'CFLAGS=-DLOCKF_BROKEN' to the makefile, and alter
  189. X'setup' so that DATA_FILE_DIRECTORY has global write permission.  [2]
  190. X
  191. XThe manual pages will need a few minor changes: you will need to alter the
  192. Xlist of gosip files to specify those you choose to provide.
  193. X
  194. XOf course, they may be lots of portability problems.  I don't know which
  195. Xsystem calls are specific to this version of UNIX (SunOS 4.0.1).
  196. X
  197. XInstallation
  198. X============
  199. X
  200. XTo install gosip, run "setup gosip", where gosip should be replaced with
  201. Xwhatever files you wish to install.  Setup will create the directory for data
  202. Xfiles if it doesn't already exist - note that it must be owned by you.  The
  203. Xdirectory for data files should not be used to keep anything other than what
  204. Xgosip puts there.
  205. X
  206. XProblems
  207. X========
  208. X
  209. XAs I've already said, I really don't know what system calls will or will not
  210. Xwork on your system.  I will be pleased to receive bug reports or suggestions
  211. Xfor modifications mailed to 'mauhk@uk.ac.warwick'.  However, since term ends
  212. Xon 1 July 1989 and I will go home then, I will not be able to reply to mail
  213. Xreceived after that date.  I do, though, expect to come back for a day at some
  214. Xpoint during the summer so I will still see any mail sent.
  215. X
  216. XAndreas Pagel                        Magician, Programmer and Mathematician
  217. XUUCP:   ...!mcvax!ukc!warwick!mauhk                   University of Warwick
  218. XJANET:  mauhk@uk.ac.warwick                           Coventry
  219. XARPA:   mauhk%cu.warwick@nss.cs.ucl.ac.uk             Great Britain
  220. X
  221. X
  222. XFootnotes
  223. X=========
  224. X
  225. X[1]  Gosip files used to be known as "the gossip files" in the manual pages
  226. X     and colloquially.  When I rewrote gosip in C, I needed to compile test
  227. X     versions.  Obviously I needed a name other than gossip by which to call
  228. X     the binary, so I chose gosip.  From this, the current usage of gosip
  229. X     arose.
  230. X
  231. X[2]  I'm not sure whether this will suffice.  I haven't got time to test that
  232. X     aspect, since I'm running out of time in which to post the program.
  233. X
  234. X[3]  Dw's name comes from 'dw gosip' meaning "deal with gosip".  I understand
  235. X     that Bourne shell functions are not universal, so this program may be
  236. X     difficult to port to some systems.
  237. END_OF_FILE
  238. if test 4961 -ne `wc -c <'README'`; then
  239.     echo shar: \"'README'\" unpacked with wrong size!
  240. fi
  241. # end of 'README'
  242. fi
  243. if test -f 'allgoss.1' -a "${1}" != "-c" ; then 
  244.   echo shar: Will not clobber existing file \"'allgoss.1'\"
  245. else
  246. echo shar: Extracting \"'allgoss.1'\" \(382 characters\)
  247. sed "s/^X//" >'allgoss.1' <<'END_OF_FILE'
  248. X.TH ALLGOSS 1 "25 February 1989"
  249. X.SH NAME
  250. Xallgoss \- prints a list of all gosip files
  251. X.SH SYNOPSIS
  252. Xallgoss
  253. X.SH DESCRIPTION
  254. Xallgoss prints a list of all gosip files.
  255. X.SH SEE ALSO
  256. Xdw(1), lu(1), gossip(1), events(1), etc.
  257. X.SH BUGS
  258. XIf the gosip file 'frog' exists, but the corresponding 'text.frog' has been
  259. Xdeleted by some unfriendly lifeform, frog will not be included in the output.
  260. END_OF_FILE
  261. if test 382 -ne `wc -c <'allgoss.1'`; then
  262.     echo shar: \"'allgoss.1'\" unpacked with wrong size!
  263. fi
  264. # end of 'allgoss.1'
  265. fi
  266. if test -f 'cat.c' -a "${1}" != "-c" ; then 
  267.   echo shar: Will not clobber existing file \"'cat.c'\"
  268. else
  269. echo shar: Extracting \"'cat.c'\" \(2794 characters\)
  270. sed "s/^X//" >'cat.c' <<'END_OF_FILE'
  271. X#include <signal.h>
  272. X#include <sys/wait.h>
  273. X
  274. X#include "global.h"
  275. X#include "cat.h"
  276. X#include "util.h"
  277. X#include "history.h"
  278. X
  279. X/*  void page()
  280. X *
  281. X *  If the stdout is connected to a terminal, page will fork off the pager in
  282. X *  the environment variable PAGER, or more(1) if PAGER is not defined.  It
  283. X *  makes all subsequent output to stdout go through the pager.  Any errors,
  284. X *  eg. from fork() or pipe(), cause an exit.  Note that the parent becomes
  285. X *  the pager, and that the return happens in the child.
  286. X */
  287. Xvoid page()
  288. X{
  289. X  int filedes[2], pid;
  290. X  char *pager = getenv ( "PAGER" ), *tmp;
  291. X
  292. X  if ( ! isatty ( 1 ) )
  293. X    return;
  294. X  error ( pipe ( filedes ), "Couldn't create pipe" );
  295. X  error ( pid = fork (), "Couldn't fork" );
  296. X  if ( pid == 0 )
  297. X  {
  298. X    error ( close ( filedes[0] ), "Close failed" );
  299. X    error ( dup2 ( filedes[1], 1 ), "dup2 failed for output" );
  300. X    error ( close ( filedes[1] ), "Close failed" );
  301. X    return;                     /* error() will cause an exit if anything */
  302. X  }                             /* fails, which closes file descriptors. */
  303. X
  304. X  error ( close ( filedes[1] ), "Close failed" );
  305. X  error ( dup2 ( filedes[0], 0 ), "dup2 failed for input" );
  306. X  error ( close ( filedes[0] ), "Close failed" );
  307. X  if ( ! pager )                /* here though, there should really be a */
  308. X    pager = "more";             /* kill(pid) before an exit, not error() */
  309. X  else                          /* get just the pager, not options */
  310. X    for ( tmp = pager; *tmp != '\0'; tmp++ )
  311. X      if ( *tmp == ' ' )
  312. X        *tmp = '\0';
  313. X  execlp ( pager, pager, (char *) 0 );
  314. X  (void) fprintf ( stderr, "Couldn't invoke your pager.\n" );
  315. X  perror ( pager );
  316. X  (void) kill ( SIGTERM, pid );
  317. X  exit ( 2 );
  318. X}
  319. X
  320. X/*  void catfile ( type )
  321. X *
  322. X *  Catfile will print the gosip file, using the program in the environment
  323. X *  variable PAGER to display it.  It forks off a process to run the pager and
  324. X *  feeds it, via a pipe, a linefeed, the existing gosip file, and another
  325. X *  linefeed.
  326. X *
  327. X *  type  :  controls the type of message passed to lastedit() and history.
  328. X */
  329. Xvoid catfile ( type )
  330. Xenum edit_type type;
  331. X{
  332. X  FILE *fp;
  333. X
  334. X  if ( type != raw )
  335. X  {
  336. X    update_history ( time ( (time_t *) 0 ), 0, type == being_edited ? edit_failed : list );
  337. X    page();
  338. X    if ( type == being_edited ) /* can't just use lastedit ( AUTO ) here, */
  339. X      lastedit ( EDIT );        /* since status might have changed. */
  340. X    else
  341. X      lastedit ( AUTO );
  342. X    (void) putchar ( '\n' );
  343. X  }
  344. X
  345. X  if ( fp = fopen ( text_file, "r" ) )
  346. X  {
  347. X    int c;
  348. X
  349. X    while ( ( c = getc ( fp ) ) != EOF )
  350. X      (void) putchar ( (char) c );
  351. X    (void) fclose ( fp );
  352. X  }
  353. X  else
  354. X  {
  355. X    (void) printf ( "<Couldn't access %s text file.>\n\n", file );
  356. X    exit ( 2 );
  357. X  }
  358. X
  359. X  if ( type != raw )
  360. X    (void) putchar ( '\n' );
  361. X  exit ( 0 );
  362. X}
  363. END_OF_FILE
  364. if test 2794 -ne `wc -c <'cat.c'`; then
  365.     echo shar: \"'cat.c'\" unpacked with wrong size!
  366. fi
  367. # end of 'cat.c'
  368. fi
  369. if test -f 'cat.h' -a "${1}" != "-c" ; then 
  370.   echo shar: Will not clobber existing file \"'cat.h'\"
  371. else
  372. echo shar: Extracting \"'cat.h'\" \(1080 characters\)
  373. sed "s/^X//" >'cat.h' <<'END_OF_FILE'
  374. X/*  The cat module contains the code that will simply print the gosip file.
  375. X */
  376. X
  377. X/*  void page()
  378. X *
  379. X *  Page causes all subsequent output to be piped via a pager.  If the
  380. X *  environment variable PAGER is set, it is used (without options), otherwise
  381. X *  more(1).
  382. X */
  383. Xextern void page();
  384. X
  385. X/*  void catfile ( type )
  386. X *
  387. X *  The function prints lastedit information, and then the gosip file itself.
  388. X *  It never returns, but calls exit().
  389. X *
  390. X *  type  :  controls the last usage message text.
  391. X */
  392. Xextern void catfile();
  393. X
  394. Xenum edit_type      /* used to specify type of edit.  */
  395. X{
  396. X  normal,           /* default edit */
  397. X  being_edited,     /* tells catfile() to send "being edited" string */
  398. X                    /* to lastedit() */
  399. X  abort,            /* tells edit() not to call catfile() if gosip is being
  400. X                       edited. */
  401. X  raw,            /* tells catfile to print gosip file without any extra
  402. X               information, and without a pager */
  403. X  cflag             /* means gosip was invoked with the c flag.  Used
  404. X                       exclusively by Geoff's program. */
  405. X};
  406. END_OF_FILE
  407. if test 1080 -ne `wc -c <'cat.h'`; then
  408.     echo shar: \"'cat.h'\" unpacked with wrong size!
  409. fi
  410. # end of 'cat.h'
  411. fi
  412. if test -f 'control.c' -a "${1}" != "-c" ; then 
  413.   echo shar: Will not clobber existing file \"'control.c'\"
  414. else
  415. echo shar: Extracting \"'control.c'\" \(4456 characters\)
  416. sed "s/^X//" >'control.c' <<'END_OF_FILE'
  417. X#include "global.h"
  418. X#include "control.h"
  419. X#include "util.h"
  420. X
  421. X/*  byte parse ( line, code, term, file )
  422. X *
  423. X *  This routine will parse the line its given, picking out usercode, terminal
  424. X *  and file.  The format of the line should be ":code,term,file!".  If term
  425. X *  is a null pointer, just the code will be returned.
  426. X *
  427. X *  Parse() returns 0 if the line didn't begin with a ':', otherwise it
  428. X *  segmentation faults if the rest of the line doesn't contain the right
  429. X *  number of commas and exclamation marks.  If it does, parse() returns 1.
  430. X *
  431. X *  line  :  points to the line to be parsed.
  432. X *  code  :  the code will be written where this points.
  433. X *  term  :  the terminal will be written where this points.
  434. X *  file  :  the file will be written where this points.
  435. X */
  436. Xstatic byte parse ( line, code, term, file )
  437. Xchar *line, *code, *term, *file;
  438. X{
  439. X  if ( *line++ != ':' )
  440. X    return 0;
  441. X  while ( *line != ',' )
  442. X    *code++ = *line++;
  443. X  *code = '\0';
  444. X  if ( ! term )
  445. X    return 1;
  446. X  while ( *++line != ',' )
  447. X    *term++ = *line;
  448. X  *term = '\0';
  449. X  while ( *++line != '!' )
  450. X    *file++ = *line;
  451. X  *file = '\0';
  452. X  return 1;
  453. X}
  454. X
  455. X/*  byte add_user()
  456. X *
  457. X *  If the gosip files are down, add_user() will check DOWN_FILE for the
  458. X *  user's code.  If he is mentioned, it will return 1.  If he isn't, add_user
  459. X *  checks he's on a terminal, and then adds a line to DOWN_FILE in the format
  460. X *  ":<usercode>,<terminal>,<file>!".
  461. X */
  462. Xbyte add_user()
  463. X{
  464. X  FILE *fp = fopen ( DOWN_FILE, "r+" );
  465. X  char line[30], *term, *ttyname();
  466. X
  467. X  if ( ! fp )
  468. X    return 0;
  469. X  (void) fseek ( fp, 0L, 0 );
  470. X  while ( fgets ( line, 29, fp ) )
  471. X  {
  472. X    char code[10];
  473. X
  474. X    if ( parse ( line, code, (char *) 0, (char *) 0 ) )
  475. X      if ( ! strcmp ( code, usercode() ) )
  476. X      {
  477. X        (void) fclose ( fp );
  478. X        return 1;
  479. X      }
  480. X  }
  481. X  if ( term = ttyname ( 2 ) )   /* get the terminal stderr is attached to */
  482. X  {                             /* stderr is least likely to be redirected */
  483. X    struct stat info;
  484. X
  485. X    (void) fprintf ( fp, ":%s,%s,%s!\n", usercode(), term, file );
  486. X    (void) fclose ( fp );
  487. X    if ( stat ( term, &info ) == 0 ) /* make user's terminal world writeable */
  488. X      (void) chmod ( term, (int) info.st_mode & 07777 | 02 ); /* so he can */
  489. X  }                             /* be told when gosip is available again */
  490. X  return 0;
  491. X}
  492. X
  493. X/*  void inform_users()
  494. X *
  495. X *  This routine will attempt to write a message to each of the users in the
  496. X *  DOWN_FILE, to tell them that gosip is up again.
  497. X */
  498. Xvoid inform_users()
  499. X{
  500. X  FILE *fp = fopen ( DOWN_FILE, "r" );
  501. X  char line[40];
  502. X
  503. X  if ( ! fp )
  504. X    return;
  505. X  while ( fgets ( line, 40, fp ) )
  506. X  {
  507. X    char code[10], term[12], file[15];
  508. X    FILE *wr;
  509. X
  510. X    if ( ! parse ( line, code, term, file ) )
  511. X      continue;
  512. X    (void) printf ( "Informing %s ...", code );
  513. X    (void) fflush ( stdout );
  514. X    if ( wr = fopen ( term, "w" ) )
  515. X    {
  516. X      struct stat info;
  517. X
  518. X      (void) fprintf ( wr, "\007\r\n** The %s file is now in service again. **\r\n", file );
  519. X      (void) fclose ( wr );
  520. X      if ( stat ( term, &info ) == 0 ) /* futile attempt to protect the */
  521. X        (void) chmod ( term, (int) info.st_mode & 07775 ); /* terminal again */
  522. X      puts ( " done." );
  523. X    }
  524. X    else
  525. X      puts ( " couldn't write his terminal." );
  526. X  }
  527. X  (void) fclose ( fp );
  528. X}
  529. X
  530. X/*  void check_if_up()
  531. X *
  532. X *  Checks for the presence of DOWN_FILE.  If it exists, it indicates that
  533. X *  gosip is down.  If the user is SUPER_USER, it just prints a warning
  534. X *  message, otherwise it calls add_user() and prints an appropiate message
  535. X *  depending on the return value.
  536. X */
  537. Xvoid check_if_up()
  538. X{
  539. X  if ( access ( DOWN_FILE, F_OK ) == -1 )
  540. X    return;    /* ought really to check that owner == SUPER_USER */
  541. X  else
  542. X    if ( ! strcmp ( usercode(), SUPER_USER ) )
  543. X    {
  544. X      (void) printf ( "Warning: %s is down.\n\n", file );
  545. X      return;
  546. X    }
  547. X  if ( add_user() )
  548. X  {
  549. X    (void) printf ( "\007I've already told you that the %s files are down.\n", file );
  550. X    puts ( "You'll be told when service is restored, provided your terminal is writeable." );
  551. X  }
  552. X  else
  553. X  {
  554. X    FILE *fp = fopen ( DOWN_FILE, "r" );
  555. X    char mes[80], *temp;
  556. X    extern char *rindex();
  557. X
  558. X    (void) fgets ( mes, 80, fp );
  559. X    if ( temp = rindex ( mes, '\n' ) )
  560. X      *temp = '\0';
  561. X    (void) printf ( "Sorry, the %s file is down for maintenance.\n", file );
  562. X    (void) printf ( "\t... %s.\n", mes );
  563. X    puts ( "You will probably be informed when it returns to service." );
  564. X  }
  565. X  exit ( 1 );
  566. X}
  567. END_OF_FILE
  568. if test 4456 -ne `wc -c <'control.c'`; then
  569.     echo shar: \"'control.c'\" unpacked with wrong size!
  570. fi
  571. # end of 'control.c'
  572. fi
  573. if test -f 'control.h' -a "${1}" != "-c" ; then 
  574.   echo shar: Will not clobber existing file \"'control.h'\"
  575. else
  576. echo shar: Extracting \"'control.h'\" \(1486 characters\)
  577. sed "s/^X//" >'control.h' <<'END_OF_FILE'
  578. X/*  This module contains routines that can be used to stop people from using
  579. X *  gosip.  This would be needed, for example, if a new version was to be
  580. X *  installed which used a different lock mechanism.
  581. X */
  582. X
  583. X/*  byte add_user()
  584. X *  
  585. X *  If the gosip files are down, add_user() will check DOWN_FILE for the
  586. X *  user's code.  If he is mentioned, it will return 1.  If he isn't,
  587. X *  add_user() checks he's on a terminal, and then adds a line to DOWN_FILE in
  588. X *  the format ":<usercode>,<terminal>,<file>!".
  589. X */
  590. Xextern byte add_user();
  591. X
  592. X/*  void inform_users()
  593. X *
  594. X *  This routine will attempt to write a message to each of the users in the
  595. X *  DOWN_FILE, to tell them that gosip is up again.
  596. X */
  597. Xextern void inform_users();
  598. X
  599. X/*  void check_if_up()
  600. X *
  601. X *  Checks for the presence of DOWN_FILE.  It's existance would indicate that
  602. X *  gosip is down.  If the user is SUPER_USER, it just prints a warning
  603. X *  message, otherwise an entry is added to DOWN_FILE, if necessary, and an
  604. X *  appropiate message printed.
  605. X */
  606. Xextern void check_if_up();
  607. X
  608. X#ifdef REAL
  609. X#define DOWN_FILE "/tmp/sutech" /* DOWN_FILE exists when gosip is down. */
  610. X#else REAL                      /* It is created by the -# option and */
  611. X#define DOWN_FILE "/tmp/frog"   /* removed by the -@ option. */
  612. X#endif REAL
  613. X
  614. X#define SUPER_USER "mauhk"      /* SUPER_USER is immune to gosip being down */
  615. X                                /* and is the only one who can use the -# */
  616. X                                /* and -@ options. */
  617. END_OF_FILE
  618. if test 1486 -ne `wc -c <'control.h'`; then
  619.     echo shar: \"'control.h'\" unpacked with wrong size!
  620. fi
  621. # end of 'control.h'
  622. fi
  623. if test -f 'dw.1' -a "${1}" != "-c" ; then 
  624.   echo shar: Will not clobber existing file \"'dw.1'\"
  625. else
  626. echo shar: Extracting \"'dw.1'\" \(2672 characters\)
  627. sed "s/^X//" >'dw.1' <<'END_OF_FILE'
  628. X.TH DW 1 "10 June 1989"
  629. X.SH NAME
  630. Xdw - show changes in the gosip files.
  631. X.SH SYNOPSIS
  632. X.B dw [<gosip file> [<gosip file> ...]]
  633. X.SH DESCRIPTION
  634. XDw uses diff(1) to show changes in the specified gosip files.  This
  635. Xsaves you having to wade through 40K of file just to find that the
  636. Xonly change is a spelling correction.  On its own, dw will show
  637. Xcontext diffs for all gosip files (obtained from allgoss(1)); with
  638. Xarguments, differences for those files only.  Note that the diffs are
  639. Xrun asynchronously, so doing dw for a lot of files will push the load
  640. Xaverage up quite a bit.
  641. X.sp
  642. XThe copies of the gosip files are kept in $HOME/.backups.  If you do
  643. Xnot have such a directory, dw will not work.
  644. X.SH USAGE
  645. XDw first copies all the specified gosip files into $HOME/.backups, and
  646. Xrun diff(1) in the background for each.  It then loops through all
  647. Xgosip files, until it finds one for which diff has completed.  The
  648. Xchanges, if any, are displayed, followed by the last four lines of
  649. Xhistory information for that file.  You are then prompted for a command.
  650. X.TP
  651. X.B b
  652. XBackup the new version of the current gosip file.  Equivalent to just
  653. Xpressing return.
  654. X.TP
  655. X.B n
  656. XDo not back this file up.
  657. X.TP
  658. X.B c
  659. XBack this file up and check it again.  You might want to use this
  660. Xcommand if the history information shows that the file has been edited
  661. Xin the last few minutes.
  662. X.TP
  663. X.B C
  664. XCheck this file again but do not back it up first.
  665. X.TP
  666. X.B e
  667. XBackup this file, then edit it.  Dw will use gosip -c, with the extra
  668. Xfile being the context diffs.  This flag causes gosip to use emacs to edit
  669. Xthe file, and load the context diffs into a separate buffer.
  670. X.sp
  671. XWhen the edit finishes, dw will automatically recheck that file.  You
  672. Xmight say, why not just back up the new version, since you presumably
  673. Xknow what you changed, but you'd be surprised how many spelling errors
  674. Xyou spot looking over the diffs.
  675. X.TP
  676. X.B E
  677. XEdit the file but do not backup it up first.  Useful if you wish to
  678. Xreplace some text some cheese head has deleted.
  679. X.TP
  680. X.B q
  681. XBackup up the current file and quit dw.  This involves killing all
  682. Xdiffs that may still be running, and removing any temporary files.
  683. X.TP
  684. X.B Q
  685. XQuit without backing up the current file.
  686. X.TP
  687. X.B a
  688. XAdd another gosip file to those being checked.  You are prompted for
  689. Xthe gosip file you wish to add.
  690. X.SH FILES
  691. X.TP 20
  692. X.B $HOME/.backups
  693. XDirectory containing gosip file backups.
  694. X.SH SEE ALSO
  695. Xallgoss(1), lu(1), gossip(1).
  696. X.SH AUTHOR
  697. XAndreas Pagel, at Warwick University, UK.
  698. X.SH BUGS
  699. XThe tidying routine called by the quit option not work.  I have no
  700. Xidea what is wrong, but quitting will entirely fail to delete all the
  701. Xfiles it should, or indeed to do anything useful at all.
  702. END_OF_FILE
  703. if test 2672 -ne `wc -c <'dw.1'`; then
  704.     echo shar: \"'dw.1'\" unpacked with wrong size!
  705. fi
  706. # end of 'dw.1'
  707. fi
  708. if test -f 'edit.c' -a "${1}" != "-c" ; then 
  709.   echo shar: Will not clobber existing file \"'edit.c'\"
  710. else
  711. echo shar: Extracting \"'edit.c'\" \(10183 characters\)
  712. sed "s/^X//" >'edit.c' <<'END_OF_FILE'
  713. X#include <signal.h>
  714. X#include <sys/wait.h>
  715. X#include <sys/time.h>
  716. X#include <sys/resource.h>
  717. X
  718. X#include "edit.h"
  719. X#include "global.h"
  720. X#include "cat.h"
  721. X#include "control.h"
  722. X#include "util.h"
  723. X#include "history.h"
  724. X
  725. X#include <dirent.h>
  726. X#include <ctype.h>
  727. X
  728. X#define DEFAULT_EDITOR "ded"
  729. X#define MIN_AGE 4*60*60         /* minimum age of drivel files in secs */
  730. X
  731. X#define smaller(a,b) a * 10 < b * 7 || b - a > 1700
  732. X
  733. Xextern char *strcat(), *rindex(), *strncpy();
  734. Xextern int strncmp();
  735. X
  736. Xstatic int editor;              /* pid of editor child */
  737. Xstatic byte edit_over = 0;      /* flag set by SIGCHLD */
  738. Xstatic char *text_copy;         /* file name of text_file's copy */
  739. X
  740. X/*  void tidy_up()
  741. X *
  742. X *  Routine called on receiving a terminating signal, such as TERM, QUIT or
  743. X *  HUP.  It releases locks, and removes backup files before quitting.  Also
  744. X *  kills the editor.
  745. X */
  746. Xstatic void tidy_up()
  747. X{
  748. X  (void) kill ( editor, SIGTERM );
  749. X  (void) lock ( REMVE );
  750. X  (void) unlink ( text_copy );
  751. X  (void) fprintf ( stderr, "\r\n\007** %s: received terminating signal - quitting.\n", file );
  752. X  exit ( 3 );
  753. X}
  754. X
  755. X/*  void force_off()
  756. X *
  757. X *  This routine is called when SIGALRM is received.  It prints a final
  758. X *  warning to the user, and then kills his editor after waiting a further
  759. X *  short time.  This will cause him to stop using gosip.
  760. X */
  761. Xstatic void force_off()
  762. X{
  763. X  puts ( "\r\n\007** Edit session terminating almost immediately. **\r" );
  764. X  sleep ( 5 );
  765. X  (void) kill ( editor, SIGTERM );
  766. X}
  767. X
  768. X/*  void child()
  769. X *
  770. X *  Called whenever a SIGCHLD is received.  It checks if the editor has
  771. X *  actually exit()ed - it might have changed state by being suspended - and
  772. X *  sets the global flag edit_over if it has.
  773. X */
  774. Xstatic void child()
  775. X{
  776. X  if ( editor == wait3 ( (union wait *) 0, WNOHANG, (struct rusage *) 0 ) )
  777. X    edit_over = 1;
  778. X}
  779. X
  780. X/*  void do_nothing()
  781. X *
  782. X *  This routine does literally nothing.  It is needed as you have to call
  783. X *  something when you get SIGALRM.
  784. X */
  785. Xstatic void do_nothing()
  786. X{
  787. X}
  788. X
  789. X/*  void manifesto()
  790. X *
  791. X *  Checks for the file info.gosip and prints its contents.
  792. X *  Currently unimplemented.
  793. X */
  794. X/*
  795. Xstatic void manifesto()
  796. X{
  797. X  FILE *info_file;
  798. X  char info_filename[MAX_LENGTH];
  799. X  int c;
  800. X
  801. X  (void) strcpy ( info_filename, "info." );
  802. X  (void) strcat ( info_filename, file );
  803. X  if ( info_file = fopen ( info_filename, "r" ) )
  804. X  {
  805. X    while ( ( c = getc ( info_file ) ) != EOF )
  806. X      (void) putchar ( (char) c );
  807. X    (void) fclose ( info_file );
  808. X  }
  809. X}
  810. X*/
  811. X
  812. X/*  char *copy ( source, destination )
  813. X *
  814. X *  Copies the source file into the destination file.  If destination is null,
  815. X *  a filename in /tmp is generated and used as destination.  Returns the
  816. X *  destination file on success, otherwise a null pointer.
  817. X *
  818. X *  source       :  filename from which to copy.
  819. X *  destination  :  filename to which to copy.
  820. X */
  821. Xstatic char *copy ( source, destination )
  822. Xchar *source, *destination;
  823. X{
  824. X  FILE *sp, *dp;
  825. X  int c;
  826. X
  827. X  if ( ! destination )
  828. X  {
  829. X    char temp[6];
  830. X
  831. X    (void) strncpy ( temp, file, 5 );
  832. X    temp[5] = '\0';
  833. X    destination = tempnam ( "/tmp", temp );
  834. X  }
  835. X  if ( ! destination )
  836. X  {
  837. X    perror ( "Couldn't get a temp file" );
  838. X    return (char *) 0;
  839. X  }
  840. X  if ( ! ( sp = fopen ( source, "r" ) ) )
  841. X  {
  842. X    perror ( "Couldn't open source file" );
  843. X    return (char *) 0;
  844. X  }
  845. X  if ( ! ( dp = fopen ( destination, "w" ) ) )
  846. X  {
  847. X    perror ( "Couldn't open destination file" );
  848. X    (void) fclose ( sp );
  849. X    return (char *) 0;
  850. X  }
  851. X  while ( ( c = getc ( sp ) ) != EOF )
  852. X    if ( putc ( (char) c, dp ) == EOF )
  853. X    {
  854. X      puts ( "Couldn't copy to destination file." );
  855. X      (void) fclose ( dp );
  856. X      (void) fclose ( sp );
  857. X      return (char *) 0;
  858. X    }
  859. X  (void) fclose ( sp );
  860. X  if ( fclose ( dp ) == EOF )
  861. X  {
  862. X    perror ( "Error closing destination file" );
  863. X    return (char *) 0;
  864. X  }
  865. X  return destination;
  866. X}
  867. X
  868. X/*  byte changed ( i_size )
  869. X *
  870. X *  This will compare the text_file against the copy, and returns 0 if they
  871. X *  are identical in content, otherwise 1.  That is, it also returns 1 if
  872. X *  either file couldn't be read or some other error occured.  It also
  873. X *  contains code checking for an excessive reduction in the size of the copy,
  874. X *  which asks the user to confirm that he wants to keep the smaller file.
  875. X *
  876. X *  i_size  :  inital size of text_file.
  877. X */
  878. Xstatic byte changed ( i_size )
  879. Xint i_size;
  880. X{
  881. X  FILE *tp, *cp;
  882. X  int c;
  883. X  byte result = 0;
  884. X  struct stat final;
  885. X  
  886. X  if ( stat ( text_copy, &final ) == 0 )
  887. X    if ( smaller ( final.st_size, i_size ) )
  888. X    {
  889. X      char ans[10];
  890. X      
  891. X      while ( 1 )             /* leave loop by 'break' */
  892. X      {
  893. X        (void) printf ( "The %s file is now somewhat smaller - from %d down to %d bytes.\nDo you want to keep it like this? ", file, i_size, final.st_size );
  894. X        result = 1;
  895. X        if ( ! fgets ( ans, 10, stdin ) )
  896. X          *ans = '\0';
  897. X        if ( ! strncmp ( "no", ans, 2 ) )
  898. X        {
  899. X          (void) unlink ( text_copy );
  900. X          (void) free ( text_copy );
  901. X          result = 0;
  902. X          break;
  903. X        }
  904. X        if ( ! strncmp ( "yes", ans, 3 ) )
  905. X        {
  906. X          result = 2;         /* flag proceed as normal */
  907. X          break;
  908. X        }
  909. X        puts ( "Answer 'yes' or 'no'.\n" );
  910. X      }
  911. X      if ( result < 2 )
  912. X      {
  913. X        (void) unlink ( text_copy );
  914. X        (void) free ( text_copy );
  915. X        return result;
  916. X      }
  917. X    }
  918. X
  919. X  if ( result < 2 )
  920. X  {
  921. X    if ( ! ( tp = fopen ( text_file, "r" ) ) )
  922. X      result = 1;
  923. X    else
  924. X    {
  925. X      if ( ! ( cp = fopen ( text_copy, "r" ) ) )
  926. X        result = 1;
  927. X      else
  928. X      {
  929. X        do
  930. X        {
  931. X          if ( ( c = getc ( tp ) ) != getc ( cp ) )
  932. X          {
  933. X            result = 1;
  934. X            break;
  935. X          }
  936. X        }
  937. X        while ( c != EOF );
  938. X        (void) fclose ( cp );
  939. X      }
  940. X      (void) fclose ( tp );
  941. X    }
  942. X  }
  943. X
  944. X  if ( ! copy ( text_copy, text_file ) )
  945. X    result = 0;
  946. X  (void) unlink ( text_copy );
  947. X  (void) free ( text_copy );
  948. X  return result;
  949. X}
  950. X
  951. X/*  void do_edit ( kind, extra_file )
  952. X *
  953. X *  This is the routine that actually lets you edit the gosip file.  It
  954. X *  fork()s off a process which becomes the editor.  While that is running it
  955. X *  check every ten seconds whether the DOWN_FILE exists, and stops the edit
  956. X *  if it does.
  957. X *
  958. X *  It also updates last_file & history_file, checks for files that shouldn't
  959. X *  be in the directory and removes them, prints info about the file (not yet
  960. X *  implemented), and deals with the file already being edited - normally by
  961. X *  calling catfile(), but see desciption of 'kind' below.
  962. X *
  963. X *  kind        :  specifes the type of edit.  It is an enumerated type
  964. X *                 covering
  965. X *                   normal - straight edit
  966. X *                   abort - do not invoke catfile() if gosip is being edited.
  967. X *                   cflag - use emacs with extra_file as editor.
  968. X *  extra_file  :  this file is passed to emacs as well as text_file if kind
  969. X *                 is cflag.  This is only used by Geoff's program.
  970. X */
  971. Xvoid do_edit ( kind, extra_file )
  972. Xenum edit_type kind;
  973. Xchar *extra_file;
  974. X{
  975. X  int mask, init_size = -1;
  976. X  FILE *fp;
  977. X  time_t now;
  978. X  struct stat info;
  979. X  byte lock_status;
  980. X
  981. X  mask = sigblock ( sigmask ( SIGALRM ) | sigmask ( SIGCHLD ) );
  982. X  (void) signal ( SIGINT, SIG_IGN );
  983. X  (void) signal ( SIGCHLD, child );
  984. X  (void) signal ( SIGTERM, tidy_up );
  985. X  (void) signal ( SIGHUP, tidy_up );
  986. X  (void) signal ( SIGQUIT, tidy_up );
  987. X  if ( ( lock_status = lock ( CREAT ) ) == -1 )
  988. X  {                             /* error creating lock */
  989. X    (void) printf ( "Please refer to your local %s maintainer.\n", file );
  990. X    exit ( 2 );
  991. X  }
  992. X  if ( lock_status )
  993. X  {                             /* gosip is already being edited */
  994. X    if ( kind == abort || kind == cflag )
  995. X    {
  996. X      lastedit ( EDIT );
  997. X      exit ( 1 );
  998. X    }
  999. X    else
  1000. X      catfile ( being_edited );
  1001. X  }
  1002. X  lastedit ( INFO );
  1003. X  (void) putchar ( '\n' );
  1004. X
  1005. X  if ( fp = fopen ( last_file , "w" ) )
  1006. X  {                             /* put user into last_file */
  1007. X    (void) fprintf ( fp, "%s (%s)\n", gosname(), usercode() );
  1008. X    if ( fclose ( fp ) == -1 )
  1009. X      perror ( "Problem closing last file" );
  1010. X  }
  1011. X  else
  1012. X    perror ( "Couldn't write last usage info" );
  1013. X
  1014. X  (void) time ( &now );
  1015. X#if 0
  1016. X  manifesto();                  /* print info about this gosip file */
  1017. X#endif
  1018. X  if ( stat ( text_file, &info ) == 0 )
  1019. X    init_size = info.st_size;   /* store initial size for history info. */
  1020. X  if ( ! ( text_copy = copy ( text_file, (char *) 0 ) ) )
  1021. X    exit ( 2 );                 /* make copy which is actually edited */
  1022. X
  1023. X  if ( ( editor = fork() ) == -1 )
  1024. X  {
  1025. X    perror ( "Fork failed" );
  1026. X    (void) lock ( REMVE );
  1027. X    (void) unlink ( text_copy );
  1028. X    exit ( 2 );
  1029. X  }
  1030. X  if ( editor == 0 )
  1031. X  {
  1032. X    char *visual;
  1033. X
  1034. X    if ( ! ( visual = getenv ( "VISUAL" ) ) )
  1035. X      if ( ! ( visual = getenv ( "EDITOR" ) ) )
  1036. X        visual = DEFAULT_EDITOR;
  1037. X    if ( kind == cflag )
  1038. X      execlp ( visual, visual, extra_file, text_copy, (char *) 0 );
  1039. X    else
  1040. X      execlp ( visual, visual, text_copy, (char *) 0 );
  1041. X    (void) fprintf ( stderr, "Couldn't invoke your editor.\n" );
  1042. X    perror ( visual );
  1043. X    exit ( 2 );
  1044. X  }
  1045. X
  1046. X  (void) signal ( SIGALRM, do_nothing );
  1047. X  while ( ! edit_over )         /* wait for child to die */
  1048. X  {
  1049. X    static byte mes_delivered = 0;
  1050. X    FILE *fp;
  1051. X
  1052. X    if ( ! mes_delivered )
  1053. X      (void) alarm ( 10 );
  1054. X    (void) sigpause ( mask );
  1055. X    if ( ! ( mes_delivered || edit_over ) )
  1056. X      if ( fp = fopen ( DOWN_FILE, "r" ) )
  1057. X      {
  1058. X        char mes[80], *temp;
  1059. X
  1060. X        (void) fgets ( mes, 79, fp );
  1061. X        if ( temp = rindex ( mes, '\n' ) )
  1062. X          *temp = '\0';
  1063. X        (void) fclose ( fp );
  1064. X        (void) printf ( "\r\n\007** %s is going down for maintenance. **\r\n", file );
  1065. X        (void) printf ( "      ... %s.  \r\n", mes );
  1066. X        puts ( "** Please stop editing now. **\r" );
  1067. X        (void) printf ( "** You will probably be informed when %s is available again. **\r\n", file );
  1068. X        mes_delivered = 1;
  1069. X        (void) add_user();
  1070. X        (void) signal ( SIGALRM, force_off );
  1071. X        (void) alarm ( 50 );
  1072. X      }
  1073. X  }
  1074. X
  1075. X  (void) alarm ( 0 );
  1076. X  (void) sigsetmask ( mask );
  1077. X  update_history ( now, init_size, changed ( init_size ) ? edit : no_change );
  1078. X  (void) lock ( REMVE );
  1079. X
  1080. X  exit ( 0 );
  1081. X}
  1082. END_OF_FILE
  1083. if test 10183 -ne `wc -c <'edit.c'`; then
  1084.     echo shar: \"'edit.c'\" unpacked with wrong size!
  1085. fi
  1086. # end of 'edit.c'
  1087. fi
  1088. if test -f 'edit.h' -a "${1}" != "-c" ; then 
  1089.   echo shar: Will not clobber existing file \"'edit.h'\"
  1090. else
  1091. echo shar: Extracting \"'edit.h'\" \(673 characters\)
  1092. sed "s/^X//" >'edit.h' <<'END_OF_FILE'
  1093. X/*  void doedit ( kind, extra_file )
  1094. X *
  1095. X *  This routine handles editing of the gosip file.  It takes care of all
  1096. X *  bookkeeping, and does not return - it calls exit().
  1097. X *
  1098. X *  kind        :  specifes the type of edit.  It is an enumerated type
  1099. X *                 covering
  1100. X *                   normal - straight edit
  1101. X *                   abort - do not invoke catfile() if gosip is being edited.
  1102. X *                   cflag - use emacs and extra_file as editor.
  1103. X *                 see also "cat.h"
  1104. X *  extra_file  :  this file is passed to emacs as well as the text_file if
  1105. X *                 kind is cflag.  This is only used by Geoff's program.
  1106. X */
  1107. Xextern void do_edit();
  1108. END_OF_FILE
  1109. if test 673 -ne `wc -c <'edit.h'`; then
  1110.     echo shar: \"'edit.h'\" unpacked with wrong size!
  1111. fi
  1112. # end of 'edit.h'
  1113. fi
  1114. if test -f 'global.h' -a "${1}" != "-c" ; then 
  1115.   echo shar: Will not clobber existing file \"'global.h'\"
  1116. else
  1117. echo shar: Extracting \"'global.h'\" \(1007 characters\)
  1118. sed "s/^X//" >'global.h' <<'END_OF_FILE'
  1119. X#include <stdio.h>
  1120. X#include <sys/types.h>
  1121. X#include <sys/stat.h>
  1122. X#include <sys/file.h>
  1123. X
  1124. X#define error(result,errmes) if ( (result) == -1 ) {perror(errmes); exit(2);}
  1125. X#define MAX_GOSIP_LENGTH 8              /* max length of a gosip fileanme */
  1126. X#define MAX_FILE_LENGTH (sizeof(DATA_FILE_DIRECTORY)+MAX_GOSIP_LENGTH+13)
  1127. X            /* max. length of global file names - the additional 13 allows for
  1128. X               the longest prefix, "/Rush/text.", and terminating '\0' */
  1129. X
  1130. X#ifdef REAL
  1131. X#define DATA_FILE_DIRECTORY "/poppy/cs/upt/open/dubbin"
  1132. X#else REAL
  1133. X#define DATA_FILE_DIRECTORY "/poppy/ma/uhk/gtfd/.files"
  1134. X#endif REAL
  1135. X
  1136. X#define SECRET_DIR "/Rush"
  1137. X
  1138. Xextern char *getenv(), *rindex(), *strcpy();
  1139. Xextern time_t time();
  1140. X
  1141. Xextern char text_file[MAX_FILE_LENGTH], file[MAX_FILE_LENGTH];
  1142. Xextern char history_file[MAX_FILE_LENGTH], last_file[MAX_FILE_LENGTH];
  1143. X#ifdef LOCKF_BROKEN
  1144. Xextern char lock_file[MAX_FILE_LENGTH];
  1145. X#endif LOCKF_BROKEN
  1146. X
  1147. Xtypedef char byte;              /* use byte for small ints (-1 up to c.25) */
  1148. END_OF_FILE
  1149. if test 1007 -ne `wc -c <'global.h'`; then
  1150.     echo shar: \"'global.h'\" unpacked with wrong size!
  1151. fi
  1152. # end of 'global.h'
  1153. fi
  1154. if test -f 'gosip.1' -a "${1}" != "-c" ; then 
  1155.   echo shar: Will not clobber existing file \"'gosip.1'\"
  1156. else
  1157. echo shar: Extracting \"'gosip.1'\" \(3737 characters\)
  1158. sed "s/^X//" >'gosip.1' <<'END_OF_FILE'
  1159. X.TH GOSSIP 1 "25 February 1989"
  1160. X.SH NAME
  1161. Xelection, events, faith, flames, games, gossip, help, jokes, musik, politics,
  1162. Xsmut, suggest, xian - Print or edit the relevant file.
  1163. X.SH SYNOPSIS
  1164. X.B gosip [-e|-l|-p[<printer>]|-r|-s|-t|-v|-x]
  1165. X.SH DESCRIPTION
  1166. XGosip (general term meaning any one of events, flames, etc.) allows you to
  1167. Xedit a text file containing material contributed by other gosip users.  When
  1168. Xinvoked without flags, it tells you who last edited the file, then calls up
  1169. Xthe editor in the environment variable VISUAL, failing that EDITOR, or vi if
  1170. Xthat doesn't exist either.  At this point you edit the file, enjoying other
  1171. Xpeople's contributions, and adding your own as you wish.  When you quit the
  1172. Xeditor, gosip will store details of your edit, which you can examine using lu,
  1173. Xbefore ending.
  1174. X.sp
  1175. XYour name is taken from the environment variable GOSNAME, if defined,
  1176. Xotherwise from the variable NAME.
  1177. X.sp
  1178. XCertain general unwritten rules of etiquette regarding the editing of gosip
  1179. Xfiles have evolved over time.
  1180. X.IP +
  1181. XLeave your name or alias after your contribution.  Many people will
  1182. Xwrite '*name*' or something similar after unattributed contributions, or even
  1183. Xdelete them.  There is a list of names and aliases near the beginning of the
  1184. Xgossip file; feel free to add yours there.
  1185. X.IP +
  1186. XDoing global replaces, eg. turning all instance of 'and' to '&', is considered
  1187. Xpretty tedious.
  1188. X.IP +
  1189. XDon't delete other people's contributions, just because you don't like them.
  1190. XTo keep the size of the files reasonable, removing older material is OK, but
  1191. Xtry to leave some context for any comments following those you delete.
  1192. X.IP +
  1193. XAltering what other people have written to make them look silly isn't
  1194. Xconsidered clever or witty, but anti-social.
  1195. X.IP +
  1196. XMost people find it rather boring to read the same thing several times, so
  1197. Xdon't put an indentical piece of text in more than one gosip files.
  1198. X.PP
  1199. XNote that the symbol :-) (called a smiley face) or some variation is often
  1200. Xplaced beside text that is intended to be of a non-serious nature.
  1201. X.SH OPTIONS
  1202. X.TP
  1203. X.B \-e
  1204. XIf the gosip file is being edited, this is reported without listing it.
  1205. X.TP
  1206. X.B \-l
  1207. XList the gosip file instead of editing it.  The program in the environment
  1208. Xvariable PAGER is used to display it, or more(1) if PAGER is not defined.
  1209. X.TP
  1210. X.B \-p[<printer>]
  1211. XSend the gosip file to the (specified) printer.
  1212. X.TP
  1213. X.B \-r
  1214. XInform the user as soon as gosip becomes free for editing.
  1215. X.TP
  1216. X.B \-s
  1217. XReturn exit status 1 if the file is being edited, or zero if it isn't, without
  1218. Xproducing any output.
  1219. X.TP
  1220. X.B \-t
  1221. XReport whether gosip is being edited or not.
  1222. X.TP
  1223. X.B \-v
  1224. XPrint the version number.  Details of changes corresponding to each version
  1225. Xnumber are currently available in ~mauhk/gtfd/doc/notes - at least at
  1226. XWarwick.
  1227. X.TP
  1228. X.B \-x
  1229. XAct as though called without arguments.  It's use is that it overrides any
  1230. Xdefault argument in GOSIP.
  1231. X.SH DEFAULT OPTIONS
  1232. XIf no flag (or -x) is present, the program will attempt to edit the file.  If,
  1233. Xhowever, gosip is being edited by someone else, it will be printed instead.
  1234. X.sp
  1235. XFor historical reasons, any single letter is equivalent to the '-l'
  1236. Xoption, eg. 'gosip s' == 'gosip -l'.
  1237. X.sp
  1238. XYou can use the environment variable GOSIP to store a default option for
  1239. Xgosip.  That is, if gosip is invoked without flags, the flag stored in GOSIP
  1240. Xwill be used.
  1241. X.SH SEE ALSO
  1242. Xallgoss(1), lu(1), dw(1).
  1243. X.SH AUTHOR
  1244. XOriginal concept by Jonathan Hughes.
  1245. X.br
  1246. XBasically written by Andreas Pagel.
  1247. X.br
  1248. XSubstantially modified by Geoff Rimmer.
  1249. X.br
  1250. XCompletely rewritten by Mike Taylor.
  1251. X.br
  1252. XCompletely re-rewritten by Andreas Pagel.
  1253. X.SH BUGS
  1254. XIf you are near your hard quota when trying to edit gosip, you might
  1255. Xend up not being able to save it or even deleting it, so be careful.
  1256. END_OF_FILE
  1257. if test 3737 -ne `wc -c <'gosip.1'`; then
  1258.     echo shar: \"'gosip.1'\" unpacked with wrong size!
  1259. fi
  1260. # end of 'gosip.1'
  1261. fi
  1262. if test -f 'history.c' -a "${1}" != "-c" ; then 
  1263.   echo shar: Will not clobber existing file \"'history.c'\"
  1264. else
  1265. echo shar: Extracting \"'history.c'\" \(5270 characters\)
  1266. sed "s/^X//" >'history.c' <<'END_OF_FILE'
  1267. X#include "global.h"
  1268. X#include "util.h"
  1269. X#include "history.h"
  1270. X
  1271. X#define RECENT 395724           /* entries older than this are pruned */
  1272. X#define MIN_LINES 40            /* history.gosip is not pruned to less than */
  1273. X                                /* this number of lines.  */
  1274. X/*  static FILE *his_prune()
  1275. X *
  1276. X *  This routine will prune history.gosip.  It first works out the number of
  1277. X *  entries it currently contains, and subtracts MIN_LINES to get the maximum
  1278. X *  number of entries to remove.  It then reads in each entry, and copies it
  1279. X *  back into the file only if it is less than RECENT seconds old.
  1280. X *
  1281. X *  If history.gosip fails to exist, his_prune() will try to create it.
  1282. X *
  1283. X *  A pointer to history.gosip is returned, or a null pointer in case of
  1284. X *  errors.
  1285. X */
  1286. Xstatic FILE *his_prune()
  1287. X{
  1288. X  FILE *his_file;
  1289. X  long start, end;
  1290. X  HIS_ENTRY entry;
  1291. X  time_t now;
  1292. X  int removable = 1;          /* number of removable lines in history.gosip */
  1293. X  struct stat info;
  1294. X
  1295. X  (void) time ( &now );
  1296. X  if ( stat ( history_file, &info ) == 0 )
  1297. X    removable = 1 + info.st_size / sizeof ( HIS_ENTRY ) - MIN_LINES;
  1298. X  if ( ! ( his_file = fopen ( history_file, "a+" ) ) )
  1299. X  {
  1300. X    perror ( "Couldn't open history file" );
  1301. X    return (FILE *) 0;
  1302. X  }
  1303. X  start = end = 0L;
  1304. X  if ( fseek ( his_file, end, 0 ) == -1 )
  1305. X  {
  1306. X    perror ( "Error seeking in history file" );
  1307. X    (void) fclose ( his_file );
  1308. X    return (FILE *) 0;
  1309. X  }
  1310. X  while ( fread ( (char *) &entry, sizeof ( entry ), 1, his_file ) )
  1311. X  {
  1312. X    end = ftell ( his_file );
  1313. X    if ( now - entry.date < RECENT || removable-- < 1 )
  1314. X    {
  1315. X      if ( fseek ( his_file, start, 0 ) == -1 )
  1316. X      {
  1317. X        perror ( "Error seeking in history file" );
  1318. X        (void) fclose ( his_file );
  1319. X        return (FILE *) 0;
  1320. X      }
  1321. X      if ( fwrite ( (char *) &entry, sizeof ( entry ), 1, his_file ) < 1 )
  1322. X      {
  1323. X        perror ( "Error writing to history file" );
  1324. X        (void) fclose ( his_file );
  1325. X        return (FILE *) 0;
  1326. X      }
  1327. X      start = ftell ( his_file );
  1328. X    }
  1329. X    if ( fseek ( his_file, end, 0 ) == -1 )
  1330. X    {
  1331. X      perror ( "Error seeking in history file" );
  1332. X      (void) fclose ( his_file );
  1333. X      return (FILE *) 0;
  1334. X    }
  1335. X  }
  1336. X  if ( fseek ( his_file, start, 0 ) == -1 )
  1337. X  {
  1338. X    perror ( "Error seeking in history file" );
  1339. X    (void) fclose ( his_file );
  1340. X    return (FILE *) 0;
  1341. X  }
  1342. X  return his_file;
  1343. X}
  1344. X
  1345. X/*  void update_history ( date, size1, how )
  1346. X *
  1347. X *  This routine is used to add an entry to history.gosip.  It also
  1348. X *  automatically creates the file if it doesn't exist, and keeps it down to a
  1349. X *  reasonable size if it does.  It is called by do_edit() and catfile() to
  1350. X *  record whenever a user read or edited the gosip file.
  1351. X *
  1352. X *  Data stored consists of the time the edit commenced, the (gosip) name and
  1353. X *  usercode of the person invoking gosip and the initial and final size of
  1354. X *  the gosip file.  The final size can be replaced by one of the following:
  1355. X *          listing - the gosip file was listed, not edited
  1356. X *      edit failed - the user tried to edit the gosip file, but it was
  1357. X *                    already being edited.  This entry is only stored if
  1358. X *                    catfile() was invoked, not if gosip was called with the
  1359. X *                    '-e' option.  The idea behind this is that an entry is
  1360. X *                    only made if a user actually got to see the text of the
  1361. X *                    gosip file.
  1362. X *        unchanged - the user did not alter the gosip file during his edit.
  1363. X *                    If the backup of the gosip is somehow deleted during the
  1364. X *                    user's edit, gosip will obviously not be able to
  1365. X *                    determine whether the file has changed, and will provide
  1366. X *                    a final size.
  1367. X *
  1368. X *  First his_prune() is called to remove older entries.  It will return a
  1369. X *  file pointer to history.gosip, leaving it pointing to the correct place in
  1370. X *  the file to add the new entry.  The new entry is then contructed - some
  1371. X *  information is passed in parameters as detailed below, the rest can be
  1372. X *  worked out.
  1373. X *
  1374. X *  Finally the new entry is written and then history.gosip is truncated to
  1375. X *  reflect its new size, and chmod() is called to give it appropiate
  1376. X *  permissions.  This should only be necessary if the file was created by
  1377. X *  update_history().
  1378. X *
  1379. X *  date   :  time of access to gosip file.
  1380. X *  size1  :  initial size of file - only relevent if how == edit.
  1381. X *  how    :  type of access.
  1382. X */
  1383. Xvoid update_history ( date, size1, how )
  1384. Xtime_t date;
  1385. Xint size1;
  1386. XMODE how;
  1387. X{
  1388. X  FILE *his_file;
  1389. X  
  1390. X  if ( his_file = his_prune() )
  1391. X  {
  1392. X    HIS_ENTRY entry;
  1393. X    struct stat info;
  1394. X    long new_size;
  1395. X
  1396. X    (void) strcpy ( entry.name, gosname() );
  1397. X    (void) strcpy ( entry.user, usercode() );
  1398. X    entry.date = date;
  1399. X    entry.how = how;
  1400. X    entry.size1 = size1;
  1401. X    entry.size2 = -1;
  1402. X    if ( stat ( text_file, &info ) == 0 )
  1403. X      entry.size2 = info.st_size;
  1404. X    if ( fwrite ( (char *) &entry, sizeof ( entry ), 1, his_file ) < 1 )
  1405. X      perror ( "Write of history information failed" );
  1406. X    new_size = ftell ( his_file );
  1407. X    if ( fclose ( his_file ) )
  1408. X      perror ( "Error closing history file" );
  1409. X    if ( truncate ( history_file, new_size ) )
  1410. X      perror ( "Error adjusting size" );
  1411. X  }
  1412. X  (void) chmod ( history_file, 0666 );
  1413. X}
  1414. END_OF_FILE
  1415. if test 5270 -ne `wc -c <'history.c'`; then
  1416.     echo shar: \"'history.c'\" unpacked with wrong size!
  1417. fi
  1418. # end of 'history.c'
  1419. fi
  1420. if test -f 'history.h' -a "${1}" != "-c" ; then 
  1421.   echo shar: Will not clobber existing file \"'history.h'\"
  1422. else
  1423. echo shar: Extracting \"'history.h'\" \(1327 characters\)
  1424. sed "s/^X//" >'history.h' <<'END_OF_FILE'
  1425. X/*  void update_history ( date, size1, how )
  1426. X *
  1427. X *  This will add an entry to history.gosip if it can.  It will aso try to
  1428. X *  ensure that the file has the correct permissions, and will create it if it
  1429. X *  doesn't exist.  Some of the information it works out itself, the rest is
  1430. X *  passed as arguments.
  1431. X *
  1432. X *  It will also remove entries older than RECENT seconds from the file,
  1433. X *  provided at least MIN_LINES remain.
  1434. X *
  1435. X *  date   :  time of access to gosip file.
  1436. X *  size1  :  initial size of file - only relevent if how == edit.
  1437. X *  how    :  type of access.
  1438. X */
  1439. Xextern void update_history();
  1440. X
  1441. Xtypedef enum { edit, list, edit_failed, no_change } MODE;
  1442. X
  1443. Xtypedef struct                  /* entry in history.gosip */
  1444. X{
  1445. X  char name[MAX_NAME_LEN];      /* name of user */
  1446. X  char user[MAX_CODE_LEN];      /* code of user */
  1447. X  time_t date;                  /* time at which they used gosip */
  1448. X  int size1, size2;             /* sizes, before & after, of text.gosip */
  1449. X  MODE how;                     /* whether they edited or listed, etc. */
  1450. X} HIS_ENTRY;  
  1451. X
  1452. X/* Portability warning: note that the above struct may not be stored the way
  1453. X *  on different machines.  Here at Warwick, MAX_NAME_LEN had to carefully
  1454. X *  chosen to make the history entries compatible between the two computers
  1455. X *  from which gosip could be invoked.
  1456. X */
  1457. END_OF_FILE
  1458. if test 1327 -ne `wc -c <'history.h'`; then
  1459.     echo shar: \"'history.h'\" unpacked with wrong size!
  1460. fi
  1461. # end of 'history.h'
  1462. fi
  1463. if test -f 'lu.1' -a "${1}" != "-c" ; then 
  1464.   echo shar: Will not clobber existing file \"'lu.1'\"
  1465. else
  1466. echo shar: Extracting \"'lu.1'\" \(2055 characters\)
  1467. sed "s/^X//" >'lu.1' <<'END_OF_FILE'
  1468. X.TH LU 1 "25 February 1989"
  1469. X.SH NAME
  1470. Xlu \- Print history information for gosip files.
  1471. X.SH SYNOPSIS
  1472. X.B lu [-<int>[:<int>]] [gosip-file [gosip-file ...]]
  1473. X.SH DESCRIPTION
  1474. XWithout arguments, lu prints a summary of information about all gosip files.
  1475. XFor each gosip file, it gives the time when the last edit was commenced, its
  1476. Xcurrent size and the name and the usercode of the last person to edit it.  A
  1477. Xtrailing '*' indicates the file is currently being edited.
  1478. X.sp
  1479. XWith arguments, some of the recent editing history for each gosip file is
  1480. Xgiven.  Each line corresponds to a person looking at the text of the gosip
  1481. Xfile, and gives the time of access, the name and usercode of the person
  1482. Xinvolved, the initial size, and information on the type of access.  If two
  1483. Xnumbers are given, this implies an edit of the gosip file, where the two
  1484. Xnumbers are the initial and final sizes.
  1485. X.sp
  1486. X\'Listing' implies use of the '-l' option.  'Edit failed' means the user
  1487. Xattempted to edit the gosip file, only to find it was already being edited; if
  1488. Xthe '-e' option was used, an edit failed line is not recorded.  'Unchanged'
  1489. Ximplies that the user edited gosip without making any changes.
  1490. X.SH OPTIONS
  1491. XThe number of lines to print from the history file can be specified
  1492. Xby '-<number of lines>' or '-<hour>:<min>'.  The later form will list all
  1493. Xentries dated within the last <hour> hours and <min> minutes.  Either option
  1494. Xcan be given more than once, eg.
  1495. X.sp
  1496. X.RS
  1497. Xlu events -10 gossip -15 flames help
  1498. X.RE
  1499. X.sp
  1500. Xwhich will list 20 lines (the default) from events, 10 from gossip and 15 from
  1501. Xflames and help.
  1502. X.sp
  1503. XAlternatively, the usage exemplified by
  1504. X.sp
  1505. X.RS
  1506. Xlu -1:
  1507. X.RE
  1508. X.sp
  1509. Xwill print changes for the past hour for all gosip files.
  1510. X.sp
  1511. XHistory information is currently kept for about 3 days.
  1512. X.SH AUTHOR
  1513. XOriginally Mike Taylor.
  1514. X.br
  1515. XModified by Andreas Pagel.
  1516. X.br
  1517. XRewritten by Andreas Pagel and incoporated into gosip.
  1518. X.SH SEE ALSO
  1519. Xallgoss(1), gossip(1), dw(1).
  1520. X.SH BUGS
  1521. XThe usage 'lu -0 gossip' is accepted and treated as 'lu -1 gossip'.  Similar
  1522. Xproblems with options '-:' and '-'.
  1523. END_OF_FILE
  1524. if test 2055 -ne `wc -c <'lu.1'`; then
  1525.     echo shar: \"'lu.1'\" unpacked with wrong size!
  1526. fi
  1527. # end of 'lu.1'
  1528. fi
  1529. if test -f 'lu.c' -a "${1}" != "-c" ; then 
  1530.   echo shar: Will not clobber existing file \"'lu.c'\"
  1531. else
  1532. echo shar: Extracting \"'lu.c'\" \(9217 characters\)
  1533. sed "s/^X//" >'lu.c' <<'END_OF_FILE'
  1534. X#include "global.h"
  1535. X#include "lu.h"
  1536. X#include "cat.h"
  1537. X#include "main.h"
  1538. X#include "util.h"
  1539. X#include "history.h"
  1540. X  
  1541. X#include <dirent.h>
  1542. X#include <ctype.h>
  1543. X
  1544. Xextern char *ctime(), *malloc(), *strcat();
  1545. X
  1546. Xtypedef struct fmt
  1547. X{
  1548. X  union
  1549. X  {
  1550. X    time_t date;                /* parameter - secs */
  1551. X    int max_lines;              /* maximum number of lines */
  1552. X  } param;
  1553. X  byte time;                    /* whether time or length param */
  1554. X  struct fmt *next;
  1555. X} FORMAT;
  1556. X
  1557. Xstatic FORMAT *lines = NULL;
  1558. X
  1559. X/*  byte gosip_file ( file_name )
  1560. X *
  1561. X *  This routine examines file_name and returns 1 if it matches text.[a-z]+,
  1562. X *  which is what valid gosip files would match, and provided that [a-z]+ bit
  1563. X *  is not longer than MAX_GOSIP_LENGTH.  Zero otherwise.
  1564. X *
  1565. X *  file_name  :  points to filename to check.
  1566. X */
  1567. Xstatic byte gosip_file ( file_name )
  1568. Xchar *file_name;
  1569. X{
  1570. X  byte index;
  1571. X  char *temp;
  1572. X
  1573. X  for ( index = 0; index < 5; index++ )
  1574. X    if ( *file_name++ != "text."[index] )
  1575. X      return 0;
  1576. X  if ( ! *file_name )           /* prefix on its own is illegal */
  1577. X    return 0;
  1578. X  temp = file_name;             /* to work out length */
  1579. X  while ( *file_name != '\0' )
  1580. X    if ( ! isalpha ( *file_name++ ) )
  1581. X      return 0;
  1582. X  return file_name - temp <= MAX_GOSIP_LENGTH;
  1583. X}
  1584. X
  1585. X/*  byte allgoss ( files )
  1586. X *
  1587. X *  Returns an alphabetical list of gosip files in the array files.  It
  1588. X *  obtains this information by searching DATA_FILE_DIRECTORY for files
  1589. X *  matched by gosip_file().  It returns 0 if no files could be found, either
  1590. X *  because of an error or because there weren't any; otherwise it returns the
  1591. X *  number of files found.
  1592. X *
  1593. X *  files  :  the array in which the result is stored.
  1594. X */
  1595. Xbyte allgoss ( files )
  1596. Xchar *files[FILE_NUM];
  1597. X{
  1598. X  DIR *dirp;
  1599. X  struct dirent *dep;
  1600. X  byte num = 0, sorted = 0, index;
  1601. X  char text_dir[ sizeof ( DATA_FILE_DIRECTORY ) + sizeof ( SECRET_DIR ) ];
  1602. X
  1603. X  (void) strcpy ( text_dir, DATA_FILE_DIRECTORY );
  1604. X  (void) strcat ( text_dir, SECRET_DIR );
  1605. X  if ( ! ( dirp = opendir ( text_dir ) ) )
  1606. X  {
  1607. X    perror ( "Couldn't read directory" );
  1608. X    return 0;
  1609. X  }
  1610. X                                /* obtain list of gosip files */
  1611. X  for ( dep = readdir ( dirp ); dep; dep = readdir ( dirp ) )
  1612. X    if ( gosip_file ( dep->d_name ) )
  1613. X    {
  1614. X      if ( num >= FILE_NUM )
  1615. X      {
  1616. X        (void) fprintf ( stderr, "Too many gosip files! (Max %d).\n", FILE_NUM );
  1617. X        return 0;
  1618. X      }
  1619. X      if ( ! ( files[num] = malloc ( (unsigned) strlen ( dep->d_name ) - 4 ) ) )
  1620. X      {
  1621. X        (void) fprintf ( stderr, "No memory.\n" );
  1622. X        return 0;
  1623. X      }
  1624. X      (void) strcpy ( files[num++], dep->d_name + 5 );
  1625. X    }
  1626. X  (void) closedir ( dirp );
  1627. X  
  1628. X  while ( ! sorted )            /* sort gosip files alphabetically */
  1629. X  {
  1630. X    char *temp;
  1631. X    
  1632. X    sorted = 1; 
  1633. X    for ( index = 1; index < num; index++ )
  1634. X      if ( strcmp ( files[index-1], files[index] ) > 0 )
  1635. X      {
  1636. X        temp = files[index-1];
  1637. X        files[index-1] = files[index];
  1638. X        files[index] = temp;
  1639. X        sorted = 0;
  1640. X      }
  1641. X  }
  1642. X  if ( ! num )
  1643. X    (void) fprintf ( stderr, "No files!\n" );
  1644. X  return num;
  1645. X}
  1646. X
  1647. X/*  void history ( gosip )
  1648. X *
  1649. X *  History will list the last n lines from the file history.gosip, where n is
  1650. X *  determined by the contents of lines.
  1651. X *
  1652. X *  This is done by building up a linked list of lines, and throwing away
  1653. X *  those that don't meet the requirements.  (**write this out properly**)
  1654. X *
  1655. X *  gosip  :  the file for which history is required.
  1656. X */
  1657. X static void history ( gosip )
  1658. Xchar *gosip;
  1659. X{
  1660. X  extern char *sprintf();
  1661. X  time_t now;
  1662. X  FILE *his_file;
  1663. X  HIS_ENTRY entry;
  1664. X  char line[120];
  1665. X  int ent_num = 0;
  1666. X  struct one_entry
  1667. X  {
  1668. X    int number;                 /* used to check line number limit */
  1669. X    char text[120];             /* the line that will be printed */
  1670. X    struct one_entry *next;
  1671. X  } *first, *last, *temp;
  1672. X  
  1673. X  first = last = 0;
  1674. X  (void) time ( &now );
  1675. X  init_file_names ( gosip );
  1676. X  if ( ! ( his_file = fopen ( history_file, "r" ) ) )
  1677. X  {
  1678. X    (void) printf ( "Couldn't open history file for %s.\n", file );
  1679. X    return;
  1680. X  }
  1681. X  (void) printf ( "Editing history of %s:\n", file );
  1682. X  while ( fread ( (char *) &entry, sizeof ( entry ), 1, his_file ) )
  1683. X  {
  1684. X    ent_num++;
  1685. X    if ( lines->time == 0 || now - entry.date < lines->param.date )
  1686. X    {
  1687. X      temp = last;
  1688. X      last = (struct one_entry *) malloc ( sizeof ( struct one_entry ) );
  1689. X      if ( ! last )
  1690. X      {
  1691. X        perror ( "No memory" );
  1692. X        return;
  1693. X      }
  1694. X      if ( temp != 0  )
  1695. X        temp->next = last;
  1696. X      last->number = ent_num;
  1697. X      last->next = 0;
  1698. X
  1699. X      (void) sprintf ( line, "%3d %.16s %8s: %-27s ", ent_num, ctime ( &entry.date ), entry.user, entry.name );
  1700. X      switch ( entry.how )
  1701. X      {
  1702. X      default:
  1703. X        (void) sprintf ( last->text, "%s    ***data error***\n", line );
  1704. X        break;
  1705. X      case list:
  1706. X        (void) sprintf ( last->text, "%s%7d      listing\n", line, entry.size2 );
  1707. X        break;
  1708. X      case edit_failed:
  1709. X        (void) sprintf ( last->text, "%s%7d  edit failed\n", line, entry.size2 );
  1710. X        break;
  1711. X      case edit:
  1712. X        (void) sprintf ( last->text, "%s%7d%13d\n", line, entry.size1, entry.size2 );
  1713. X        break;
  1714. X      case no_change:
  1715. X        (void) sprintf ( last->text, "%s%7d    unchanged\n", line, entry.size2 );
  1716. X      }
  1717. X      
  1718. X      if ( ! first )
  1719. X        first = last;
  1720. X      else
  1721. X        if ( lines->time == 0 && last->number - first->number >= lines->param.max_lines )
  1722. X        {
  1723. X          temp = first->next;
  1724. X          (void) free ( (char *) first );
  1725. X          first = temp;
  1726. X        }
  1727. X    }
  1728. X  }
  1729. X  (void) fclose ( his_file );
  1730. X  
  1731. X  while ( first )
  1732. X  {
  1733. X    (void) printf ( "%s", first->text );
  1734. X    temp = first->next;
  1735. X    (void) free ( (char *) first );
  1736. X    first = temp;
  1737. X  }
  1738. X  
  1739. X  lastedit ( AUTO );
  1740. X}
  1741. X
  1742. X/*  void summary()
  1743. X *
  1744. X *  Prints up a summary of gosip file status.  It gets a list of files from
  1745. X *  allgoss(), and prints a one line summary of their current state, taking
  1746. X *  information from history.gosip and last.gosip, as well at stat()ing
  1747. X *  text.gosip.
  1748. X */
  1749. Xstatic void summary()
  1750. X{
  1751. X  char *files[FILE_NUM];
  1752. X  byte index, num;
  1753. X  
  1754. X  if ( ! ( num = allgoss ( files ) ) )
  1755. X    exit ( 2 );
  1756. X  for ( index = 0; index < num; index++ )
  1757. X  {
  1758. X    struct stat info;
  1759. X    FILE *lfp;
  1760. X    
  1761. X    init_file_names ( files[index] );
  1762. X    (void) free ( files[index] );
  1763. X    (void) printf ( "%-*s: ", MAX_GOSIP_LENGTH + 1, file );
  1764. X    if ( stat ( last_file, &info ) == 0 )
  1765. X      (void) printf ( "%.12s ", ctime ( &info.st_mtime ) + 4);
  1766. X    else
  1767. X      (void) printf ( "%-13s", "<no date>" );
  1768. X    if ( stat ( text_file, &info ) == 0 )
  1769. X      (void) printf ( "(%6d bytes ) ", info.st_size );
  1770. X    if ( lfp = fopen ( last_file, "r" ) )
  1771. X    {
  1772. X      int c;
  1773. X      
  1774. X      while ( ( c = getc ( lfp ) ) != '\n' && c != EOF )
  1775. X        (void) putchar ( c );
  1776. X      (void) fclose ( lfp );
  1777. X    }
  1778. X    else
  1779. X      (void) printf ( "<no info>" );
  1780. X    if ( lock ( CHECK ) )
  1781. X      puts ( " *" );
  1782. X    else
  1783. X      (void) putchar ( '\n' );
  1784. X  }
  1785. X  exit ( 0 );
  1786. X}
  1787. X
  1788. X/*  byte parse ( str )
  1789. X *
  1790. X *  Works out whether str is for the form '<int>' or '<int>:<int>' and updates
  1791. X *  lines accordingly.  Returns 0 on correct format, 1 otherwise.
  1792. X *
  1793. X *  str  :  points to string to be parsed.
  1794. X */
  1795. Xstatic byte parse ( str )
  1796. Xchar *str;
  1797. X{
  1798. X  int number = 0;
  1799. X  FORMAT *temp, *end = lines;
  1800. X
  1801. X  if ( ! ( temp = (FORMAT *) malloc ( sizeof ( FORMAT ) ) ) )
  1802. X  {
  1803. X    perror ( "No memory" );
  1804. X    exit ( 2 );
  1805. X  }
  1806. X  if ( lines )
  1807. X  {
  1808. X    while ( end->next )
  1809. X      end = end->next;
  1810. X    end->next = temp;
  1811. X  }
  1812. X  else
  1813. X    lines = temp;
  1814. X  temp->next = NULL;
  1815. X  temp->time = 0;
  1816. X  temp->param.max_lines = 0;
  1817. X  while ( isdigit ( *str ) )
  1818. X    number = number * 10 + *str++ - '0';
  1819. X  if ( *str == '\0' )
  1820. X  {
  1821. X    temp->param.max_lines = number;
  1822. X    return 0;
  1823. X  }
  1824. X  if ( *str++ != ':' )
  1825. X    return 1;
  1826. X  temp->time = 1;
  1827. X  temp->param.date = number * 60;
  1828. X  number = 0;
  1829. X  while ( isdigit ( *str ) )
  1830. X    number = number * 10 + *str++ - '0';
  1831. X  if ( *str != '\0' )
  1832. X    return 1;
  1833. X  temp->param.date += number;
  1834. X  temp->param.date *= 60;
  1835. X  return 0;
  1836. X}
  1837. X
  1838. X/*  void lu ( argc, argv )
  1839. X *
  1840. X *  Lu prints last usage information.
  1841. X *
  1842. X *  argc  :  argc with which gosip was invoked
  1843. X *  argv  :  argv with which gosip was invoked
  1844. X */
  1845. Xvoid lu ( argc, argv )
  1846. Xint argc;
  1847. Xchar *argv[];
  1848. X{
  1849. X  char **tempv = argv;
  1850. X  int tempc = argc;
  1851. X  byte all = 1;                 /* dual purpose flag */
  1852. X  
  1853. X  if ( argc == 1 )
  1854. X    summary();
  1855. X  
  1856. X  (void) parse ( "20" );
  1857. X  while ( ++tempv, --tempc )
  1858. X    if ( (*tempv)[0] == '-' )
  1859. X      if ( parse ( *tempv + 1 ) )
  1860. X      {
  1861. X        (void) fprintf ( stderr, "Usage: %s [-<number>[:<number>]] [<gossip file>] ...\n", file );
  1862. X        exit ( 2 );
  1863. X      }
  1864. X  
  1865. X  page();
  1866. X  while ( ++argv, --argc )
  1867. X  {
  1868. X    if ( (*argv)[0] == '-' )
  1869. X    {
  1870. X      FORMAT *temp = lines->next;
  1871. X
  1872. X      (void) free ( (char *) lines );
  1873. X      lines = temp;
  1874. X    }
  1875. X    else
  1876. X    {
  1877. X      if ( ! all )
  1878. X        (void) putchar ( '\n' );
  1879. X      history ( *argv );
  1880. X      all = 0;
  1881. X    }
  1882. X  }
  1883. X
  1884. X  if ( all )                    /* do all gossip files */
  1885. X  {
  1886. X    char *files[FILE_NUM], **fptr = files;
  1887. X    byte num;
  1888. X    
  1889. X    if ( ! ( num = allgoss ( files ) ) )
  1890. X      exit ( 2 );
  1891. X    while ( num-- )
  1892. X    {
  1893. X      if ( ! all )
  1894. X        (void) putchar ( '\n' );
  1895. X      else
  1896. X        all = 0;
  1897. X      history ( *fptr++ );
  1898. X    }
  1899. X  }
  1900. X  
  1901. X  exit ( 0 );
  1902. X}
  1903. END_OF_FILE
  1904. if test 9217 -ne `wc -c <'lu.c'`; then
  1905.     echo shar: \"'lu.c'\" unpacked with wrong size!
  1906. fi
  1907. # end of 'lu.c'
  1908. fi
  1909. if test -f 'lu.h' -a "${1}" != "-c" ; then 
  1910.   echo shar: Will not clobber existing file \"'lu.h'\"
  1911. else
  1912. echo shar: Extracting \"'lu.h'\" \(629 characters\)
  1913. sed "s/^X//" >'lu.h' <<'END_OF_FILE'
  1914. X/*  void lu ( argc, argv )
  1915. X *
  1916. X *  Lu prints last usage information, obtained from last and history files.
  1917. X *
  1918. X *  argc  :  argc with which gosip was invoked
  1919. X *  argv  :  argv with which gosip was invoked
  1920. X */
  1921. Xextern void lu();
  1922. X
  1923. X/*  byte allgoss ( files )
  1924. X *
  1925. X *  Returns an alphabetical list of gosip files in the array files.  It
  1926. X *  returns 0 if no files could be found, either because of an error or
  1927. X *  because there weren't any; otherwise it returns the number of files found.
  1928. X *
  1929. X *  files  :  the array in which the result is stored.
  1930. X */
  1931. Xextern byte allgoss();
  1932. X
  1933. X#define FILE_NUM 14             /* max number of gosip files */
  1934. END_OF_FILE
  1935. if test 629 -ne `wc -c <'lu.h'`; then
  1936.     echo shar: \"'lu.h'\" unpacked with wrong size!
  1937. fi
  1938. # end of 'lu.h'
  1939. fi
  1940. if test -f 'main.c' -a "${1}" != "-c" ; then 
  1941.   echo shar: Will not clobber existing file \"'main.c'\"
  1942. else
  1943. echo shar: Extracting \"'main.c'\" \(5550 characters\)
  1944. sed "s/^X//" >'main.c' <<'END_OF_FILE'
  1945. X#include "global.h"
  1946. X#include "cat.h"
  1947. X#include "edit.h"
  1948. X#include "control.h"
  1949. X#include "util.h"
  1950. X#include "lu.h"
  1951. X
  1952. X#define VERSION 104
  1953. X#define usage() (void) fprintf ( stderr, "Usage: %s [-p|-l|-e|-t|-s|-r|-v|-x|-z]\n", file ); exit ( 4 );
  1954. X#define check_flag() if ( argv[1][2] ) { usage() }
  1955. X
  1956. Xextern char *sprintf();
  1957. Xchar text_file[MAX_FILE_LENGTH], file[MAX_FILE_LENGTH];
  1958. Xchar history_file[MAX_FILE_LENGTH], last_file[MAX_FILE_LENGTH];
  1959. X#ifdef LOCKF_BROKEN
  1960. Xchar lock_file[MAX_FILE_LENGTH];
  1961. X#endif LOCKF_BROKEN
  1962. X
  1963. X/*  void init_file_names ( invocation )
  1964. X *
  1965. X *  This function works out the names of the files used by gosip, from the
  1966. X *  name by which it is invoked, and places them in various global variables.
  1967. X *
  1968. X *  invocation  :  points to the name by which gosip was invoked (argv[0]).
  1969. X */
  1970. Xvoid init_file_names ( invocation )
  1971. Xchar *invocation;
  1972. X{
  1973. X  char *ptr = invocation, *temp = invocation;
  1974. X
  1975. X  while ( *ptr != '\0' )
  1976. X    if (  *ptr++ == '/' )
  1977. X      temp = ptr;
  1978. X  (void) strcpy ( file, temp);
  1979. X  (void) sprintf ( history_file, "%s/HISTOrY.%s", DATA_FILE_DIRECTORY, file );
  1980. X  (void) sprintf ( text_file, "%s%s/text.%s", DATA_FILE_DIRECTORY, SECRET_DIR, file );
  1981. X  (void) sprintf ( last_file, "%s/LAsT.%s", DATA_FILE_DIRECTORY, file );
  1982. X#ifdef LOCKF_BROKEN
  1983. X  (void) sprintf ( lock_file, "%s/lLo$Ck.%s", DATA_FILE_DIRECTORY, file );
  1984. X#endif LOCKF_BROKEN
  1985. X}
  1986. X
  1987. X/*  void reserve()
  1988. X *
  1989. X *  This function prints a message as soon as the gosip file is no longer
  1990. X *  being edited.  It firsts checks if gosip is being edit.  If so, it
  1991. X *  fork()s, and the child checks for the lock every 5 seconds.
  1992. X */
  1993. Xstatic void reserve()
  1994. X{
  1995. X  int pid;
  1996. X
  1997. X  if ( ! lock ( CHECK ) )
  1998. X  {
  1999. X    (void) printf ( "The %s file is not being edited.\n", file );
  2000. X    exit ( 1 );
  2001. X  }
  2002. X  error ( pid = fork(), "fork failed" );
  2003. X  if ( pid == 0 )
  2004. X  {
  2005. X    while ( lock ( CHECK ) )
  2006. X      sleep ( 5 );
  2007. X    (void) printf ( "\007\r\nThe %s file is no longer being edited.\r\n", file );
  2008. X  }
  2009. X  else
  2010. X    (void) printf ( "You'll be informed when %s becomes available.\n", file );
  2011. X  exit ( 0 );
  2012. X}
  2013. X
  2014. Xmain ( argc, argv )
  2015. Xint argc;
  2016. Xchar *argv[];
  2017. X{
  2018. X  init_file_names ( argv[0] );
  2019. X  check_if_up();
  2020. X
  2021. X  if ( ! strcmp ( file, "lu" ) )
  2022. X    lu ( argc, argv );
  2023. X  if ( ! strcmp ( file, "allgoss" ) )
  2024. X  {
  2025. X    char *files[FILE_NUM], **fptr = files;
  2026. X    byte num = allgoss ( files );
  2027. X
  2028. X    while ( num-- )
  2029. X      puts ( *fptr++ );
  2030. X    exit ( 0 );
  2031. X  }
  2032. X
  2033. X  if ( argc == 1 )
  2034. X  {
  2035. X    char *opt = getenv ( "GOSIP" );
  2036. X
  2037. X    if ( opt )
  2038. X    {
  2039. X      argc = 2;
  2040. X      argv[1] = opt;
  2041. X    }
  2042. X  }
  2043. X  switch ( argc )
  2044. X  {
  2045. X  default:
  2046. X    usage();
  2047. X  case 1:
  2048. X    do_edit ( normal, "dummy" );
  2049. X  case 2:
  2050. X    if ( argv[1][0] == '-' )
  2051. X      switch ( argv[1][1] )
  2052. X      {
  2053. X      case '#':
  2054. X        if ( strcmp ( usercode(), SUPER_USER ) )
  2055. X          catfile ( normal );   /* pretend the option doesn't exist */
  2056. X        if ( argv[1][2] == '\0' )
  2057. X        {
  2058. X          puts ( "Use the option correctly, fishhead.\n" );
  2059. X          exit ( 3 );
  2060. X        }
  2061. X        if ( ! access ( DOWN_FILE, F_OK ) )
  2062. X        {
  2063. X          puts ( "The system is already down, fool.\n" );
  2064. X          exit ( 2 );
  2065. X        }
  2066. X        (void) printf ( "Bringing down the %s files.\n", file );
  2067. X        {                       /* set up a 'lock' file */
  2068. X          FILE *fp = fopen ( DOWN_FILE, "w" );
  2069. X
  2070. X          if ( ! fp )
  2071. X          {
  2072. X            perror ( "Couldn't create lock file" );
  2073. X            exit ( 2 );
  2074. X          }
  2075. X          (void) fprintf ( fp, "%s\n", &argv[1][2] );
  2076. X          error ( fclose ( fp ), "Couldn't close lock file" );
  2077. X          if ( chmod ( DOWN_FILE, 0666 ) == -1 )
  2078. X            perror ( "Couldn't alter permissions" );
  2079. X        }
  2080. X        (void) printf ( "The %s files are down.\n", file );
  2081. X        exit ( 0 );
  2082. X      case '@':
  2083. X        check_flag();
  2084. X        if ( strcmp ( usercode(), SUPER_USER ) )
  2085. X          catfile ( normal );   /* pretend the option doesn't exist */
  2086. X        if ( access ( DOWN_FILE, F_OK ) )
  2087. X        {
  2088. X          puts ( "The system isn't down, fool.\n" );
  2089. X          exit ( 2 );
  2090. X        }
  2091. X        inform_users();
  2092. X        if ( unlink ( DOWN_FILE ) )
  2093. X          perror ( "Couldn't remove the down file" );
  2094. X        else
  2095. X          (void) printf ( "Brought %s up again.\n", file );
  2096. X        exit ( 0 );
  2097. X      case 'c':
  2098. X        do_edit ( cflag, &argv[1][2] );
  2099. X      case 'e':
  2100. X        check_flag();
  2101. X        do_edit ( abort, "dummy" );
  2102. X      case 'l':
  2103. X        check_flag();
  2104. X        catfile ( normal );
  2105. X      case 'p':
  2106. X        (void) printf ( "Sending %s to the printer.\n", file );
  2107. X        (void) fflush ( stdout );
  2108. X        if ( argv[1][2] )
  2109. X        {
  2110. X          argv[1][1] = 'P';
  2111. X          execlp ( "lpr", "lpr", argv[1], text_file, (char *) 0 );
  2112. X        }
  2113. X        else
  2114. X          execlp ( "lpr", "lpr", text_file, (char *) 0 );
  2115. X        perror ( "Couldn't run lpr" );
  2116. X        exit ( 2 );
  2117. X      case 'r':
  2118. X        check_flag();
  2119. X        reserve();
  2120. X      case 's':
  2121. X        check_flag();
  2122. X        exit ( lock ( CHECK ) != 0);
  2123. X      case 't':
  2124. X        check_flag();
  2125. X        if ( lock ( CHECK ) )
  2126. X        {
  2127. X          lastedit ( EDIT );
  2128. X          exit ( 1 );
  2129. X        }
  2130. X        else
  2131. X        {
  2132. X          (void) printf ( "The %s file is not being edited.\n", file );
  2133. X          exit ( 0 );
  2134. X        }
  2135. X      case 'v':
  2136. X        check_flag();
  2137. X        (void) printf ( "APS %s version %d.\n", file, VERSION );
  2138. X        exit ( 0 );
  2139. X      case 'x':
  2140. X        check_flag();
  2141. X        do_edit ( normal, "dummy" );
  2142. X      case 'z':
  2143. X    check_flag();
  2144. X    catfile ( raw );
  2145. X      default:
  2146. X        usage();
  2147. X      }
  2148. X    else
  2149. X      if ( argv[1][1] == '\0' )
  2150. X        catfile ( normal );
  2151. X      else
  2152. X        usage();
  2153. X  }
  2154. X}
  2155. END_OF_FILE
  2156. if test 5550 -ne `wc -c <'main.c'`; then
  2157.     echo shar: \"'main.c'\" unpacked with wrong size!
  2158. fi
  2159. # end of 'main.c'
  2160. fi
  2161. if test -f 'main.h' -a "${1}" != "-c" ; then 
  2162.   echo shar: Will not clobber existing file \"'main.h'\"
  2163. else
  2164. echo shar: Extracting \"'main.h'\" \(310 characters\)
  2165. sed "s/^X//" >'main.h' <<'END_OF_FILE'
  2166. X/*  void init_file_names ( invocation )
  2167. X *
  2168. X *  This function works out the names of the files used by gosip, from the
  2169. X *  name by which it is invoked, and places them in various global variables.
  2170. X *
  2171. X *  invocation  :  points to the name by which gosip was invoked (argv[0]).
  2172. X */
  2173. Xextern void init_file_names();
  2174. END_OF_FILE
  2175. if test 310 -ne `wc -c <'main.h'`; then
  2176.     echo shar: \"'main.h'\" unpacked with wrong size!
  2177. fi
  2178. # end of 'main.h'
  2179. fi
  2180. if test -f 'notes' -a "${1}" != "-c" ; then 
  2181.   echo shar: Will not clobber existing file \"'notes'\"
  2182. else
  2183. echo shar: Extracting \"'notes'\" \(15168 characters\)
  2184. sed "s/^X//" >'notes' <<'END_OF_FILE'
  2185. X[  Note for non Warwick readers:  poppy and orchid are the local machines on
  2186. X   which gosip is installed.  The dubbin directory is the DATA_FILE_DIRECTORY
  2187. X   which started life as 'dubbin and broken glass', but then changed to simply
  2188. X   dubbin.  ap.  ]
  2189. X
  2190. X
  2191. XThings still to do include:
  2192. X
  2193. X1.  Check the process is not suspended indefinitely.  (Fork() and alarm.)
  2194. X2.  Informing users after going down should be improved, or removed.
  2195. X3.  Move history code into history module.
  2196. X4.  Add option to skip printing of info.text. (add info.text item first :-)
  2197. X5.  Add option to lu to print out details for a given user.
  2198. X6.  Maybe keep all history information in one history file?
  2199. X
  2200. X
  2201. X[  The above list is really more for my personal benefit, but I leave it here
  2202. X   for you all to enjoy. Perhaps some enterprising programmer can implement
  2203. X   some of the things on the list, or some analyst add to them.  ap. ]
  2204. X
  2205. X
  2206. XHistory of changes by version number
  2207. X====================================
  2208. X
  2209. X29  or possibly 30 introduced a better handling of the '-r' option.
  2210. X
  2211. X31  or thereabouts saw gosip split into several modules.  It was around this
  2212. X    time that the facility to act as a primitive 'lu' was added.
  2213. X
  2214. X32  or maybe 33 corrected a bug in the automatic deletion of backup files that
  2215. X    emacs users might leave around.  This version also saw the catfile()
  2216. X    option altered, so that it didn't fork 'cat' but opened the file and
  2217. X    printed it itself.
  2218. X
  2219. X33  Introduced bug fixes for shutdown messages that were too long.  Previously
  2220. X    if the message exceeded 80 chars, gosip would segmentation fault.
  2221. X
  2222. X34  General recompilation after the makefile was hacked around a bit.  No
  2223. X    functional difference from 33 (I think).  Installed unintentionally.
  2224. X
  2225. X35  introduced proper handling of non-writable text files.  If text.gosip is
  2226. X    readable but not writable (someone would have to go and chmod it
  2227. X    deliberately), gosip will now first attempt to chmod it to 666, and,
  2228. X    failing that, copy text.gosip into a temporary file and rename it to
  2229. X    text.gosip, and then chmod it to 666.
  2230. X
  2231. X36  features a lot of changes, mainly internal.  Lu is hard linked to gosip,
  2232. X    however it is not yet fully functional.  The history file has changed to
  2233. X    an internal format.  The extra line feed coming with last edit information
  2234. X    has gone.
  2235. X
  2236. X37  has improvements to lu.  Lu can take an unlimited number of args, and will
  2237. X    print out history details for each gosip file passed as an arg.  The -n
  2238. X    flag is recognised, but ignored.
  2239. X
  2240. X38  has some improvements to the history mechanism.  The size of text.gosip is
  2241. X    now stored even if access was read-only.  History.gosip will have the
  2242. X    correct permissions if it is created by gosip.  Previously if created by a
  2243. X    read-only operation, it wouldn't have.
  2244. X
  2245. X39  has a new version of lu invoked without options.  It now produced roughly
  2246. X    the same output as the old 'lu -a' did.
  2247. X
  2248. X40  improved the information held in history.gosip.  Gosip now keeps a copy of
  2249. X    text.gosip as you edit, and then checks whether you have actually changed
  2250. X    the file or not.  This information is recorded in the history file.
  2251. X
  2252. X41  has an improved layout for lu invoked without options.  It now resembles
  2253. X    the original 'lu -a' much more.
  2254. X
  2255. X42  has a check on the size of text.gosip.  If it is significantly less than
  2256. X    at the start of the edit session, gosip asks the user whether he's sure
  2257. X    that's what he wants, and offers to restore the backup.
  2258. X
  2259. X43  has better checking of the command line for lu.  It is first scanned for
  2260. X    any invalid flags, in which case a usage message is given.  Previous
  2261. X    behaviour was to complain about invalid flags as it came to them.
  2262. X
  2263. X44  removes a bug: if a file shrinks a lot, and the user confirms he wants to
  2264. X    keep it, gosip would record the history information as "no change".  This
  2265. X    has now been corrected.
  2266. X
  2267. X45  has a minor improvement in that if you just press return in answer to the
  2268. X    question about whether you want to keep the shorter file, gosip would hang
  2269. X    until a second return was pressed.  Also, if you chose to restore the old
  2270. X    version, text.gosip would end up with permission 0400.
  2271. X
  2272. X46  saw improvements to lu.  It now skips files like "text.gosip~" when
  2273. X    printing its summary information.
  2274. X
  2275. X47  has the usercode field in the history struct changed to 12 bytes in
  2276. X    length, to avoid alignment problems between orchid and poppy.
  2277. X
  2278. X48  has a few minor redundancies removed.  It also has lu improved such that
  2279. X    it no longer leaves an unwritable history.gosip file around if one didn't
  2280. X    exist.
  2281. X
  2282. X49  has an improved message about reductions in the size of the file.  It now
  2283. X    tells you the new size when asking you whether to keep it.
  2284. X
  2285. X50  The summary provided by lu is now sorted alphabetically by gosip file.
  2286. X
  2287. X51  has quite a few minor changes.  Lu summary mode now checks properly for
  2288. X    there being too many gosip files.  The 'going down' message has a tab
  2289. X    replaced by blanks for extra readability, and waiting times before killing
  2290. X    the editor have been altered slightly.
  2291. X
  2292. X52  has one minor change (hardly worth a new version number really).  Lu
  2293. X    summary information now right aligns the size of the files, causing the
  2294. X    names that follow to be aligned.
  2295. X
  2296. X53  contains additions to the lock mechanism, using (as previously) flock() on
  2297. X    the local machine but a lock file for the remote machine.  All lock code
  2298. X    as been moved to the function lock().
  2299. X
  2300. X54  has a small bug removed.  If a lock file already exists when someone tries
  2301. X    to use gosip, it would exit with an error message about not being able to
  2302. X    create the lock file.
  2303. X
  2304. X55  has -n option working on lu.
  2305. X
  2306. X56  Whenever gosip is asked to check whether it is invoked, it will delete any
  2307. X    local lock file that may exist, if flock() says that gosip's not being
  2308. X    used locally.  This helps to minimise damage when lock files are left
  2309. X    around by means I don't fully understand - it never happens to me.
  2310. X
  2311. X57  The usage 'lu -n <int>' as been replaced by 'lu -<int>'.  This also
  2312. X    removes the bug whereby 'lu -n gosip' is not caught as an error.
  2313. X
  2314. X58  introduces 'lu -<int>' which works as 'lu -<int> `allgoss`'.
  2315. X
  2316. X59  introduces 'lu -n: gosip' which lists entries from the history file that
  2317. X    are less than n minutes old.
  2318. X
  2319. X60  now has 'lu -hour:min gosip' working.  Still no checking for invalid
  2320. X    command line arguments.
  2321. X
  2322. X61  has proper checking for invalid args to lu.
  2323. X
  2324. X62  removes the gratuitous blank line at the beginning of 'lu gosip' output.
  2325. X
  2326. X63  takes default gosip options from the environment variable GOSIP.
  2327. X
  2328. X64  improves signal handling, trapping SIGTERM and SIGQUIT and calling a tidy
  2329. X    up routine.
  2330. X
  2331. X65  prompts for a name when editing, if environment variable NAME is not
  2332. X    defined.
  2333. X
  2334. X66  improves handling of the above facility.  fgets() is used to read the
  2335. X    name, which handles delete correctly, and more info is provided.
  2336. X
  2337. X67  has alterations to the above (minor), a couple of wording changes and use
  2338. X    of environment variable EDITOR if VISUAL is undefined.
  2339. X
  2340. X68  introduces environment variable GOSNAME.  If undefined, NAME is used as
  2341. X    your name instead.
  2342. X
  2343. X69  has the lu usage message corrected.  I know it's a pretty minor
  2344. X    alteration, but I want to try installing a new version, now that I've
  2345. X    moved the binaries to one of my directories.
  2346. X
  2347. X70  Gosip prints a list of gosip files when invoked with the name 'allgoss'.
  2348. X    A few other minor changes have also occurred.
  2349. X
  2350. X71  has a few internal changes - structure in lu now has a union to ensure the
  2351. X    correct type, depending on whether a time or line number constraint is
  2352. X    required.  Previously the same field was used, which worked ok since
  2353. X    time_t == int on this system.  Main external changes are that
  2354. X    update_history() now removes from history.gosip entries more than RECENT
  2355. X    seconds old, and you can now do 'gosip -p<printer>' to specify a printer.
  2356. X
  2357. X72  removes a small inefficiency from the allgoss section, which meant that
  2358. X    allgoss() was accidentally called twice.
  2359. X
  2360. X73  increases RECENT a little.  Introduces a new module, history.[ch], to
  2361. X    which a couple of functions from lu are moved.
  2362. X
  2363. X74  The recent religious discussion in events, which meant events was invoked
  2364. X    much more frequently than is normal, has revealed deficiencies in the
  2365. X    lock() function.  It has now been rewritten to just use a simple lock
  2366. X    file.  Since SIGHUP, SIGTERM, SIGQUIT are now trapped, instances of the
  2367. X    lock file being left around when it shouldn't be, should be rare.
  2368. X
  2369. X75  Includes the name of the gosip file in the advisory message when SIGHUP,
  2370. X    etc., are caught.  The message is now sent to stderr rather than stdout.
  2371. X
  2372. X76  reintroduces flock() to the lock() function.  However, this time the
  2373. X    lock_file is always checked first, and flock() is only used if lock_file
  2374. X    is more than 12 seconds old.
  2375. X
  2376. X77  introduces an extra space in the bytes field of lu summary.  This keeps
  2377. X    the names aligned, now that xian (Christian) file has exceeded 100K.
  2378. X
  2379. X78  includes the user's name/alias in the history file information.  A
  2380. X    function gosname() has been added, similar to usercode(), that returns
  2381. X    this information.  It is also given by history() in lu.
  2382. X
  2383. X79  has the function monitor_usage() included.  It keeps a record of how gosip
  2384. X    is invoked and by whom.  I want to see exactly how many people know about
  2385. X    which options.
  2386. X
  2387. X80  If gosip is invoked by SUPER_USER, he is prompted for the removal of any
  2388. X    files in DATA_FILE_DIRECTORY which do not begin with either "lock.",
  2389. X    "history.", "last." or "text." and which are older than the oldest lock
  2390. X    file and at least half an hour old.
  2391. X
  2392. X81  ups the value of RECENT a little.  Also has a newline added between the
  2393. X    prompts for whether to delete old files, and old files now need to be at
  2394. X    least four hours old.
  2395. X
  2396. X82  has the bit that checks if you want to keep a smaller gosip file altered.
  2397. X    Since Steve was complaining that he typed 'lu' ahead and so lost his
  2398. X    changes, I've fixed it so that only responses beginning with 'yes' or 'no'
  2399. X    are accepted.
  2400. X
  2401. X83  ensures that history file pruning leaves at least MIN_LINES entries in the
  2402. X    history file.
  2403. X
  2404. X84  checks for non-printable characters in the user's name.  If the name came
  2405. X    from an environment variable, the user is prompted to enter a name.  If he
  2406. X    entered an invalid name, his name is set to "<UNKNOWN>".  An invalid
  2407. X    option is no longer treated as '-l'.
  2408. X
  2409. X85  due to popular demand, the anomaly removed in 84 is partially restored, in
  2410. X    as much as a single letter is treated as '-l'.
  2411. X
  2412. X86  features changes to usercode().  The code is obtained from the password
  2413. X    file entry for getuid(), rather than using getlogin().  This means the
  2414. X    correct person is returned if the user has used su(1).
  2415. X
  2416. X87  Changed the fopen(..., "w+") to "a+" when updating history information.
  2417. X    This means that the history file is created if it doesn't exist.
  2418. X
  2419. X88  removes a slight bug, in that '-sdfa' would be treated as '-l' rather than
  2420. X    as an illegal usage.
  2421. X
  2422. X89  improves the removal of drivel files routine.  Now also files with a legit
  2423. X    prefix ("text." or "last.") are killed if the rest of their name contains
  2424. X    non-alphabetic characters (eg. text.gosip~).  Also, history and lock files
  2425. X    are moved into a non-readable sub-directory of DATA_FILE_DIRECTORY, to
  2426. X    stop people editing/removing them.
  2427. X
  2428. X90  corrects the bug which failed to check the return value of lock() for the
  2429. X    possibility of failure in creating the lock file.
  2430. X
  2431. X91  corrects clean_directory(), in that files beginning "history." and "lock."
  2432. X    are no longer valid, now that they are stored in the sub directory.
  2433. X    Monitor_usage() includes the hostname.
  2434. X
  2435. X92  Lots of internal changes.  I went through the source to check for errors
  2436. X    before posting it, and found lots.  In some places, return values weren't
  2437. X    checked properly for errors, in others harmless errors caused exit()s, and
  2438. X    in some places, inefficient code was left over from the beginning, when I
  2439. X    was still relatively inexperienced with C.
  2440. X
  2441. X93  Installs removal of drivel files.  Now files that do not match the emacs
  2442. X    regexp "^\(text\.\|last\.\)[a-zA-Z]+$" are automatically removed by anyone
  2443. X    who uses gosip for editing (that is, not by gosip -v or -l, etc.).  I have
  2444. X    temporarily arranged to have to have a log kept of all files so deleted.
  2445. X
  2446. X94  Altered usercode() to strcpy() the code it gets from getpwuid().  Since I
  2447. X    call getpwuid() again to get the code of the owner of a drivel file so I
  2448. X    can log it (see version 93), usercode()'s value was previously
  2449. X    overwritten.
  2450. X
  2451. X95  Places a limit on the length of a gosip file.  The recent creation of the
  2452. X    'politics' gosip file messed up lu output a little, and very long names
  2453. X    would completely destroy it.
  2454. X
  2455. X96  Removes the bits that keep a records of usage (monitor_usage()) and of
  2456. X    deleted files.  Alters clean_directory() such that "Makefil" is a valid
  2457. X    prefix.  This pacifies Mike, who keeps trying to put a Makefile in
  2458. X    DATA_FILE_DIRECTORY.  MAX_FILES is upped to 14, as we now have 13 gosip
  2459. X    files.
  2460. X
  2461. X97  Alters the locking mechanism to use lockf().  Last_file is now just opened
  2462. X    for writing, not deleted, since all files in dubbin now have to be owned
  2463. X    by someone on poppy because orchid users don't have a poppy quota.
  2464. X
  2465. X98  Copies the text file into /tmp, and edits the copy.  This single change is
  2466. X    so simple and yet so powerful.  It now allows the dubbin directory to be
  2467. X    unwritable, and at a stroke removes a the need for all the tedious
  2468. X    checking for text.gosip unwritable, for drivel files in the dubbin
  2469. X    directory, etc; and allows the restructuring mentioned in version 100.
  2470. X    Further, chdir() is not called - all filenames are given with a full
  2471. X    pathname.
  2472. X
  2473. X99  Moves last_file into the .data directory.
  2474. X
  2475. X100 Alters the structure of the dubbin directory.  Dubbin is unreadable,
  2476. X    allowing last, lock and history files to be stored there, while a
  2477. X    subdirectory of dubbin is readable, in which the text files are stored.
  2478. X    This means the the directory can be searched, to allow allgoss to work,
  2479. X    yet the text files cannot be edited directly.
  2480. X
  2481. X101 Introduces the option 'z' to print out the gosip file directly - no pager
  2482. X    is invoked, nor is last usage information printed.  Also, page() is
  2483. X    altered in that it does not fork() a pager if stdout is not connected to a
  2484. X    terminal.
  2485. X
  2486. X102 I've discovered that lockf() has a bug: testing for a lock does not work
  2487. X    on orchid if the lock was applied on poppy.  Therefore I've changed lock()
  2488. X    so that it applies a lock in order to check, and then removes it.  Also,
  2489. X    if gosip is smaller, both initial and final sizes are given in the
  2490. X    confirmation message.
  2491. X
  2492. X103 Alters the error message produced by catfile() so that it doesn't give
  2493. X    away the name of the hidden directory in which text.gosip is stored.
  2494. X
  2495. X104 I found that I'd written the current name of the secret subdirectory of
  2496. X    DATA_FILE_DIRECTORY directly into the code for allgoss().  I've now
  2497. X    defined SECRET_DIR and use that instead.
  2498. END_OF_FILE
  2499. if test 15168 -ne `wc -c <'notes'`; then
  2500.     echo shar: \"'notes'\" unpacked with wrong size!
  2501. fi
  2502. # end of 'notes'
  2503. fi
  2504. if test -f 'setup' -a "${1}" != "-c" ; then 
  2505.   echo shar: Will not clobber existing file \"'setup'\"
  2506. else
  2507. echo shar: Extracting \"'setup'\" \(2586 characters\)
  2508. sed "s/^X//" >'setup' <<'END_OF_FILE'
  2509. X#! /bin/sh
  2510. X#
  2511. X# Simple prog to install a gosip file.  Give names of new gosip files as
  2512. X# arguments, and remember that only alphabetic characters are allowed.
  2513. X
  2514. X#  The following things should be customised before use.  Don't forget to
  2515. X#  alter the source as well.
  2516. X
  2517. Xsource_dir=/poppy/ma/uhk/gtfd       # location of gosip sources
  2518. Xdata_dir=/poppy/ma/uhk/gtfd/.files  # location of data files
  2519. Xsecret_dir=.data                    # subdirectory of the above
  2520. X
  2521. Xhistory=HISTOrY                     # prefix for history file (no period)
  2522. Xlast=LAsT                           # prefix for last file (also no period)
  2523. X
  2524. Xtrap '{ echo Installation failed.; exit; }' 30
  2525. X
  2526. Xif test $# -eq 0
  2527. Xthen
  2528. X  echo "Usage: $0 <gosip-file> [<gosip-file> ...]"
  2529. X  exit
  2530. Xfi
  2531. X
  2532. Xcat << zhuanshukuan
  2533. X
  2534. XGosip Installation
  2535. X==================
  2536. X
  2537. XThis program shoud be the last one you run to install gosip.  By the time you
  2538. Xrun it, you should already have customised the source code and this program.
  2539. XIt can also be used at any time to add in more gosip files.
  2540. X
  2541. XThere will be problems if you already have a program called 'lu' in the
  2542. Xdirectory where you intend to keep the binaries.  If lu does not exist, the
  2543. Xprogram assumes that no gosip files exist yet, and will compile gosip, and
  2544. Xcreate a data directory.
  2545. X
  2546. Xzhuanshukuan
  2547. X
  2548. Xecho -n "Where should the binaries go? "
  2549. Xread bin_dir
  2550. Xcd $bin_dir
  2551. Xwhile test $# -gt 0
  2552. Xdo
  2553. X  gosip=$1
  2554. X  shift
  2555. X  echo "Installing new gosip-file: $gosip."
  2556. X  if test -f lu
  2557. X  then
  2558. X    ln lu $gosip                      #  link in new binary
  2559. X  else
  2560. X    echo ''
  2561. X    echo "If this is not the first gosip file, something's wrong."
  2562. X    echo "(Maybe you've lost lu?)"
  2563. X    echo ''
  2564. X    cd $source_dir
  2565. X    make real || kill -USR1 $$        #  compile gosip
  2566. X    cd $bin_dir
  2567. X    mv $source_dir/real lu || kill -USR1 $$
  2568. X    ln lu allgoss                     #  create lu and allgoss
  2569. X    ln lu $gosip                      #  as well as the requested gosip file
  2570. X    (
  2571. X      mkdir $data_dir                 #  Doesn't matter if the directory
  2572. X      cd $data_dir                    #  already exists
  2573. X      chmod 711 . || kill -USR1 $$    #  use 733 if LOCKF_BROKEN is defined.
  2574. X      mkdir $secret_dir
  2575. X      chmod 755 $secret_dir || kill -USR1 $$
  2576. X    )
  2577. X  fi
  2578. X  (                                 #  now create the data files
  2579. X    cd $data_dir
  2580. X    touch $history.$gosip || kill -USR1 $$
  2581. X    chmod 666 $history.$gosip || kill -USR1 $$
  2582. X    touch $last.$gosip || kill -USR1 $$
  2583. X    chmod 666 $last.$gosip || kill -USR1 $$
  2584. X    cd $secret_dir
  2585. X    touch text.$gosip || kill -USR1 $$
  2586. X    chmod 666 text.$gosip || kill -USR1 $$
  2587. X  ) 
  2588. Xdone
  2589. Xecho Done.
  2590. END_OF_FILE
  2591. if test 2586 -ne `wc -c <'setup'`; then
  2592.     echo shar: \"'setup'\" unpacked with wrong size!
  2593. fi
  2594. chmod +x 'setup'
  2595. # end of 'setup'
  2596. fi
  2597. if test -f 'util.c' -a "${1}" != "-c" ; then 
  2598.   echo shar: Will not clobber existing file \"'util.c'\"
  2599. else
  2600. echo shar: Extracting \"'util.c'\" \(9807 characters\)
  2601. sed "s/^X//" >'util.c' <<'END_OF_FILE'
  2602. X#include "global.h"
  2603. X#include "util.h"
  2604. X
  2605. X#include <errno.h>
  2606. X#include <ctype.h>
  2607. X#include <pwd.h>
  2608. X#include <unistd.h>
  2609. X
  2610. X#ifdef LOCKF_BROKEN
  2611. Xextern long gethostid();
  2612. X#endif LOCKF_BROKEN
  2613. X
  2614. Xextern char *strncpy();
  2615. X
  2616. X/*  char *usercode()
  2617. X *
  2618. X *  Usercode() tries to return the usercode of the person using gosip.  It
  2619. X *  uses getuid() to obtain the id, then obtains the usercode from the
  2620. X *  password file. It returns two question marks if this doesn't succeed.
  2621. X */
  2622. Xchar *usercode()
  2623. X{
  2624. X  static char text[MAX_CODE_LEN], *code = (char *) 0;
  2625. X  
  2626. X  if ( ! code )
  2627. X  {
  2628. X    struct passwd *pwent = getpwuid ( getuid() );
  2629. X
  2630. X    if ( ! pwent )
  2631. X      return " ?? ";
  2632. X    (void) strncpy ( text, pwent->pw_name, MAX_CODE_LEN );
  2633. X    text[MAX_CODE_LEN - 1] = '\0';
  2634. X    code = text;
  2635. X  }
  2636. X  return code;
  2637. X}
  2638. X
  2639. X/*  byte valid ( name )
  2640. X *
  2641. X *  Scans string pointed to by name, returning zero if it contains any
  2642. X *  non-printing characters, 1 otherwise.
  2643. X *
  2644. X *  name  :  string to be scanned
  2645. X */
  2646. Xstatic byte valid ( name )
  2647. Xchar *name;
  2648. X{
  2649. X  while ( *name )
  2650. X    if ( ! ( isascii ( *name ) && isprint ( *name++ ) ) )
  2651. X      return 0;
  2652. X  return 1;
  2653. X}
  2654. X
  2655. X/*  char *gosname()
  2656. X *
  2657. X *  This will return the user's name, taken from the environment variable
  2658. X *  GOSNAME, or NAME if GOSNAME is not defined.  If neither is defined, or the
  2659. X *  name so obtained contains non-printable characters, the user is prompted
  2660. X *  to enter his name.
  2661. X */
  2662. Xchar *gosname()
  2663. X{
  2664. X  static char *name = 0, input[MAX_NAME_LEN];
  2665. X
  2666. X  if ( ! name )
  2667. X  {
  2668. X    if ( ! ( name = getenv ( "GOSNAME" ) ) ) 
  2669. X      name = getenv ( "NAME" );
  2670. X    if ( ! name || ! valid ( name ) )
  2671. X    {
  2672. X      puts ( "If you wish to remain anonymous, just press return." );
  2673. X      (void) printf ( "Please enter your name: " );
  2674. X      if ( ! fgets ( input, MAX_NAME_LEN, stdin ) )
  2675. X        name = "<UNKNOWN>";
  2676. X      else
  2677. X      {
  2678. X        input[strlen ( input ) - 1] = '\0';
  2679. X        if ( *input == '\0' )
  2680. X          name = "<ANON>";
  2681. X        else
  2682. X          name = input;
  2683. X      }
  2684. X    }
  2685. X    else
  2686. X    {
  2687. X      (void) strncpy ( input, name, MAX_NAME_LEN );
  2688. X      name = input;
  2689. X    }
  2690. X    name[MAX_NAME_LEN - 1] = '\0';    /* ensure name is not too long */
  2691. X    if ( ! valid ( name ) )
  2692. X      name = "<UNKNOWN>";
  2693. X  }
  2694. X  return name;
  2695. X}
  2696. X
  2697. X#ifdef LOCKF_BROKEN
  2698. X/*  static byte try_flock()
  2699. X *
  2700. X *  This provides a secondary locking mechanism.  The trouble with lock files
  2701. X *  is that they can often be left around.  I've never quite understood why -
  2702. X *  the only obvious cause would be if the computer crashed or gosip was sent
  2703. X *  a SIGKILL - but it happened with annoying regularity.  Now a lock_file has
  2704. X *  is flock()ed as well, but this flock() can only be checked for on the same
  2705. X *  computer as it was applied.
  2706. X *
  2707. X *  Hence the lock_file contains the hostid of the computer from which gosip
  2708. X *  was invoked.  This function checks whether that hostid matches the one of
  2709. X *  the current machine.  If so, it checks for the flock if the lock_file is
  2710. X *  more than 12 seconds old.  This delay is much more than is needed, and
  2711. X *  serves to allow a flock() to be applied to a freshly created lock_file.
  2712. X *  This is needed if two people invoke gosip at the same time.
  2713. X *
  2714. X *  Now a flock() is guaranteed to be removed when a process dies, therefore
  2715. X *  if a lock_file older than 12 seconds exists without a flock on it on the
  2716. X *  same machine as the one on which it was created, it is deemed to be
  2717. X *  incorrect and removed.  This system has so far worked very well, with no
  2718. X *  complaints of either more than one person editing a particular gosip file,
  2719. X *  or of a free file being considered unfree.
  2720. X *
  2721. X *  This system does depend on gosip being used fairly regularly on every
  2722. X *  machine from which it is available, which is not a problem here.
  2723. X *
  2724. X *  If the lock was valid, 1 is returned: 0 if the lock_file was removed.
  2725. X */
  2726. Xstatic byte try_flock()
  2727. X{
  2728. X  struct stat info;
  2729. X  long host;
  2730. X  int fd;
  2731. X  byte retval = 1;
  2732. X
  2733. X  if ( ( fd = open ( lock_file, O_RDONLY ) ) != -1 )
  2734. X    if ( read ( fd, (char *) &host, sizeof ( host ) ) == sizeof ( host ) )
  2735. X      if ( host == gethostid() )
  2736. X        if ( fstat ( fd, &info ) == 0 )
  2737. X          if ( time ( (time_t *) 0 ) - info.st_mtime > 12 )
  2738. X            if ( flock ( fd, LOCK_SH | LOCK_NB ) != -1 )
  2739. X            {
  2740. X              retval = 0;
  2741. X              (void) unlink ( lock_file );
  2742. X            }
  2743. X  (void) close ( fd );
  2744. X  return retval;
  2745. X}
  2746. X
  2747. X/*  byte lock ( type )
  2748. X *
  2749. X *  General function to manipulate locks on the gosip file to prevent
  2750. X *  simultaneous editing.  The parameter type controls the exact behaviour.
  2751. X *  If type is REMVE a previously applied lock will be removed, which involves
  2752. X *  removing the lock_file and the flock() on history.gosip.
  2753. X *
  2754. X *  Type CHECK will first check for a lock file.  If present, 1 is returned if
  2755. X *  try_flock() agrees the lock is genuine.  Otherwise, 0 is returned to
  2756. X *  indicate gosip is free for use.
  2757. X *
  2758. X *  Type CREAT makes similar checks as CHECK, but it will create a lock if
  2759. X *  there isn't one.  That is it creates lock_file and uses flock() to give it
  2760. X *  an exclusive lock.  It returns 0 on success, and 1 if a lock already
  2761. X *  existed.  Return value of -1 if the lock could not be created.
  2762. X *
  2763. X *  type  :  type of function to perform as detailed above.
  2764. X */
  2765. Xbyte lock ( type )
  2766. Xbyte type;
  2767. X{
  2768. X  static int lfd;
  2769. X  long host;
  2770. X
  2771. X  switch ( type )
  2772. X  {
  2773. X  case REMVE:
  2774. X    if ( ! lock ( CHECK ) )
  2775. X    {
  2776. X      puts ( "Internal error: tried to remove non-existant lock." );
  2777. X      return -1;
  2778. X    }
  2779. X    (void) close ( lfd );
  2780. X    if ( unlink ( lock_file ) )
  2781. X      perror ( "Couldn't remove lock file" );
  2782. X    return 0;
  2783. X  case CREAT:
  2784. X    lfd = open ( lock_file, O_CREAT | O_EXCL | O_WRONLY, 0644 );
  2785. X    if ( lfd == -1 )
  2786. X      if ( errno == EEXIST )
  2787. X        if ( try_flock() ) 
  2788. X          return 1;             /* valid lock exists */
  2789. X        else
  2790. X          return lock ( CREAT ); /* lock was invalid, try again */
  2791. X      else
  2792. X      {
  2793. X        perror ( "Couldn't create lock file" );
  2794. X        return -1;
  2795. X      }
  2796. X    if ( flock ( lfd, LOCK_EX | LOCK_NB ) )
  2797. X    {
  2798. X      perror ( "Flock failed" );
  2799. X      (void) fprintf ( stderr, "This may be a bug in %s!\n", file );
  2800. X      (void) lock ( REMVE );
  2801. X      return -1;
  2802. X    }
  2803. X    if ( fchmod ( lfd, 0644 ) )
  2804. X      perror ( "Couldn't alter lock file permissions" );
  2805. X    host = gethostid();
  2806. X    if ( write ( lfd, (char *) &host, sizeof ( host ) ) < sizeof ( host ) )
  2807. X      perror ( "Error writing host id" );
  2808. X    return 0;
  2809. X  case CHECK:
  2810. X    if ( access ( lock_file, F_OK ) == 0 )
  2811. X      return try_flock();
  2812. X    else
  2813. X      return 0; 
  2814. X  }
  2815. X  puts ( "Internal error: lock called with invalid argument." );
  2816. X  return -1;
  2817. X}
  2818. X
  2819. X#else LOCKF_BROKEN
  2820. X
  2821. X/*  byte lock ( type )
  2822. X *
  2823. X *  General function to manipulate locks on the gosip file to prevent
  2824. X *  simultaneous editing.  The parameter type controls the exact behaviour.
  2825. X *  If type is REMVE a previously applied lock will be removed, which involves
  2826. X *  simply releasing the lockf().
  2827. X *
  2828. X *  Type CHECK returns 1 if lockf() indicates a lock on history.gosip,
  2829. X *  otherwise 0.
  2830. X *
  2831. X *  Type CREAT creates a lock (on history.gosip) by calling flock() It returns
  2832. X *  0 on success, and 1 if a lock already existed.  Return value of -1 if the
  2833. X *  lock could not be created.
  2834. X *
  2835. X *  type  :  type of function to perform as detailed above.
  2836. X */
  2837. Xbyte lock ( type )
  2838. Xbyte type;
  2839. X{
  2840. X  static int lfd = -2, fd, ret;
  2841. X
  2842. X  switch ( type )
  2843. X  {
  2844. X  case REMVE:
  2845. X    switch ( lock ( CHECK ) )
  2846. X    {
  2847. X    case 0:
  2848. X      puts ( "Internal error: tried to remove non-existant lock." );
  2849. X      return -1;
  2850. X    case -1:
  2851. X      puts ( "Lock checking error." );
  2852. X      return -1;
  2853. X    }
  2854. X    (void) lockf ( lfd, F_ULOCK, 0L );
  2855. X    (void) close ( lfd );
  2856. X    return 0;
  2857. X  case CREAT:
  2858. X    fd = open ( history_file, O_CREAT | O_WRONLY, 0666 );
  2859. X    if ( fd == -1 )
  2860. X    {
  2861. X      perror ( "Couldn't create lock" );
  2862. X      return -1;
  2863. X    }
  2864. X    if ( lockf ( fd, F_TLOCK, 0L ) == -1 )
  2865. X      if ( errno == EAGAIN || errno == EACCES ) /* check EACCES because of */
  2866. X        return 1;                               /* lockf() bug */
  2867. X      else
  2868. X      {
  2869. X        perror ( "Lockf() failed" );
  2870. X        (void) close ( fd );
  2871. X        return -1;
  2872. X      }
  2873. X    lfd = fd;
  2874. X    return 0;
  2875. X  case CHECK:
  2876. X    if ( lfd > -2 )
  2877. X      return 1;
  2878. X    if ( ( fd = open ( history_file, O_CREAT | O_WRONLY, 0666 ) ) == -1 )
  2879. X    {
  2880. X      perror ( "Couldn't check lock" );
  2881. X      return -1;
  2882. X    }                                 /* ret = lockf ( fd, F_TEST, 0L ); */ 
  2883. X    ret = lockf ( fd, F_TLOCK, 0L );  /* cannot be used: it has a bug. */   
  2884. X    (void) close ( fd );        /* releases the lock */
  2885. X    if ( ret == -1 )
  2886. X      return ( errno == EAGAIN || errno == EACCES ) ? 1 : -1;
  2887. X    else
  2888. X      return 0; 
  2889. X  }
  2890. X  puts ( "Internal error: lock called with invalid argument." );
  2891. X  return -1;
  2892. X}
  2893. X#endif LOCKF_BROKEN
  2894. X
  2895. X/*  void lastedit ( format )
  2896. X *
  2897. X *  Lastedit prints out the name and usercode of the last person to edit the
  2898. X *  gosip file.  The exact form of the message depends on format, which also
  2899. X *  controls whether elapsed edit time is displayed.
  2900. X *
  2901. X *  format  :  indicates what type of message to print.
  2902. X */
  2903. Xvoid lastedit ( format )
  2904. Xbyte format;
  2905. X{
  2906. X  FILE *last;
  2907. X  int c;
  2908. X  time_t edit_time;
  2909. X  struct stat info;
  2910. X
  2911. X  if ( format == AUTO )
  2912. X    format = lock ( CHECK ) ? EDIT : INFO;
  2913. X  if ( last = fopen ( last_file , "r" ) )
  2914. X  {
  2915. X    if ( ( c = getc ( last ) ) != EOF )
  2916. X    {
  2917. X      if ( format == EDIT )
  2918. X        (void) printf ( "The %s file is being edited by ", file );
  2919. X      else
  2920. X        (void) printf ( "Last %s edit by ", file );
  2921. X      do
  2922. X        (void) putchar ( (char) c );
  2923. X      while ( ( c = getc ( last ) ) != '\n' && c != EOF );
  2924. X    }
  2925. X    (void) fclose ( last );
  2926. X  }
  2927. X  if ( format == EDIT)
  2928. X    if ( stat ( last_file, &info ) == 0 )
  2929. X    {
  2930. X      edit_time = time ( (time_t *) 0 ) - info.st_mtime;
  2931. X      (void) printf ( " [%d:%.2d]", edit_time / 60, edit_time % 60 );
  2932. X    }
  2933. X  puts ( "." );
  2934. X}
  2935. END_OF_FILE
  2936. if test 9807 -ne `wc -c <'util.c'`; then
  2937.     echo shar: \"'util.c'\" unpacked with wrong size!
  2938. fi
  2939. # end of 'util.c'
  2940. fi
  2941. if test -f 'util.h' -a "${1}" != "-c" ; then 
  2942.   echo shar: Will not clobber existing file \"'util.h'\"
  2943. else
  2944. echo shar: Extracting \"'util.h'\" \(1589 characters\)
  2945. sed "s/^X//" >'util.h' <<'END_OF_FILE'
  2946. X/*  The utilities module contains functions of general use, that are called by
  2947. X *  many of the other modules, yet don't really fit into any of them.
  2948. X */
  2949. X
  2950. X#define MAX_NAME_LEN 28          /* longest gosip name [returned by gosname()] */
  2951. X#define MAX_CODE_LEN 12       /* max length of usercode [from usercode()] */
  2952. X
  2953. X/*  char *usercode()
  2954. X *
  2955. X *  Returns the user's usercode if possible, otherwise '??'.
  2956. X */
  2957. Xextern char *usercode();
  2958. X
  2959. X/*  char *gosname()
  2960. X *
  2961. X *  This will return the user's chosen gosip name (GOSNAME), if defined.
  2962. X *  Otherwise NAME, or failing that, the user is prompted to enter a name.
  2963. X */
  2964. Xextern char *gosname();
  2965. X
  2966. X/*  void lastedit ( format )
  2967. X *
  2968. X *  Lastedit prints out the name and usercode of the last person to edit the
  2969. X *  gosip file.  The exact form of the message depends on type, which also
  2970. X *  controls whether elapsed edit time is displayed.
  2971. X *
  2972. X *  format  :  defines type of message, as specified below.
  2973. X */
  2974. Xextern void lastedit();
  2975. X
  2976. X#define AUTO 0                  /* use lock ( CHECK ) to decide itself */
  2977. X#define EDIT 1                  /* print 'being edited' message */
  2978. X#define INFO 2                  /* just print info */
  2979. X
  2980. X/*  byte lock ( type )
  2981. X *
  2982. X *  Returns 0 if there is no lock, 1 if there is, -1 for errors (CREAT and
  2983. X *  REMVE only).  It will also create or remove a lock as specified by type.
  2984. X *
  2985. X *  type  :  specifies whether to create a lock or not (see below).
  2986. X */
  2987. Xextern byte lock();
  2988. X
  2989. X#define CHECK 0                 /* do not alter the lock */
  2990. X#define CREAT 1                 /* create a lock */
  2991. X#define REMVE 2                 /* remove a lock */
  2992. END_OF_FILE
  2993. if test 1589 -ne `wc -c <'util.h'`; then
  2994.     echo shar: \"'util.h'\" unpacked with wrong size!
  2995. fi
  2996. # end of 'util.h'
  2997. fi
  2998. echo shar: End of shell archive.
  2999. exit 0
  3000.  
  3001.